diff --git a/config/Makefile.linux b/config/Makefile.linux
index 54d2b373e99dac3e18065a5f581e3a2895f6dd51..9da057bf9104c7efef7c9c54c57131d84178d1c2 100644
--- a/config/Makefile.linux
+++ b/config/Makefile.linux
@@ -70,8 +70,9 @@ LIBFLAGS += -lpthread -lreadline -lcurses -lrt
 # User Makefiles's $(SRC) variable is split here in sequencer source
 # and all the rest, which should be C files
 SEQ_SRCS = $(filter %.st, $(SRC))
-CSRCS = $(filter-out %.st, $(SRC))
+CSRCS = $(filter-out %.cc, $(filter-out %.st, $(SRC)))
 # Required Epics files
+CCSRCS += $(filter %.cc, $(SRC))
 CCSRCS += build/$(TARGET)/registerRecordDeviceDriver.cc
 
 # Each .st file gets preprocessed twice to make one C and one C++
diff --git a/src/drv/fmReadCoeff.c b/src/drv/fmReadCoeff.c
index 90d2410b4f75e6d80f7dc80265131018ef211377..597780b7ec3f55189eb1b30450a943b951f9afd2 100644
--- a/src/drv/fmReadCoeff.c
+++ b/src/drv/fmReadCoeff.c
@@ -30,9 +30,9 @@
 #include "fm10Gen.h"
 #include "fmReadCoeff.h"
 #include "crc.h"
+#include "util/user/check_file_crc.h"
 
 int        getwords( char*, char*[], int );
-extern int checkFileCrc( char* );
 
 /* Cat string and make upper case */
 static char*
diff --git a/src/epics/CMakeLists.txt b/src/epics/CMakeLists.txt
index 807298ee007e48af880e82bb27b64ad38ffc04db..ac390263c324215f18de77852cd54ec1f25befaf 100644
--- a/src/epics/CMakeLists.txt
+++ b/src/epics/CMakeLists.txt
@@ -1 +1,2 @@
-add_subdirectory(seq)
\ No newline at end of file
+add_subdirectory(seq)
+add_subdirectory(edcu)
diff --git a/src/epics/edcu/CMakeLists.txt b/src/epics/edcu/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..3863c819c1a3ff2a4b02ca46de1e712e230f3885
--- /dev/null
+++ b/src/epics/edcu/CMakeLists.txt
@@ -0,0 +1,36 @@
+if (Boost_FOUND)
+
+    add_executable(standalone_edc
+            standalone_edcu.cc
+            ${CMAKE_CURRENT_SOURCE_DIR}/../../drv/rfm.c)
+    #target_compile_options(standalone_edc PRIVATE -fsanitize=address)
+    #target_link_libraries(standalone_edc PRIVATE asan)
+    #target_compile_options(standalone_edc PRIVATE
+    #        -fstack-protector -fstack-protector-strong)
+    target_include_directories(standalone_edc PUBLIC
+            "${CMAKE_CURRENT_SOURCE_DIR}/../../include"
+            "${CMAKE_CURRENT_SOURCE_DIR}/../../include/drv"
+            ${Boost_INCLUDE_DIRS})
+    target_link_libraries(standalone_edc PUBLIC
+            args
+            epics::ca
+            driver::ini_parsing
+            pv::simple_pv
+            ${Boost_LIBRARIES}
+            ${CMAKE_THREAD_LIBS_INIT}
+            driver::gpsclock)
+    target_requires_cpp11(standalone_edc PUBLIC)
+
+    configure_file(test/epics_test.py ${CMAKE_CURRENT_BINARY_DIR}/epics_test.py COPYONLY)
+    configure_file(test/daqdrc_standalone_edc_live_test ${CMAKE_CURRENT_BINARY_DIR}/daqdrc_standalone_edc_live_test COPYONLY)
+    configure_file(test/test_standalone_edc_live_nds.sh.in ${CMAKE_CURRENT_BINARY_DIR}/test_standalone_edc_live_nds.sh @ONLY)
+
+    add_test(NAME test_standalone_edc_live_nds
+            COMMAND /bin/bash ./test_standalone_edc_live_nds.sh
+            WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
+
+    install(TARGETS standalone_edc DESTINATION bin)
+
+else(Boost_FOUND)
+    message(WARNING "The standalone_edc will not be build as boost was not found")
+endif (Boost_FOUND)
\ No newline at end of file
diff --git a/src/epics/seq/gps.hh b/src/epics/edcu/gps.hh
similarity index 100%
rename from src/epics/seq/gps.hh
rename to src/epics/edcu/gps.hh
diff --git a/src/epics/seq/standalone_edcu.cc b/src/epics/edcu/standalone_edcu.cc
similarity index 98%
rename from src/epics/seq/standalone_edcu.cc
rename to src/epics/edcu/standalone_edcu.cc
index e4c277b5323cdf2bdcaac8be2a12539be090ac78..f453d9cba9644ca458672d399a613d6672aa99f3 100644
--- a/src/epics/seq/standalone_edcu.cc
+++ b/src/epics/edcu/standalone_edcu.cc
@@ -58,6 +58,7 @@ extern "C" {
 #include "gps.hh"
 #include "args.h"
 #include "simple_pv.h"
+#include "util/user/check_file_crc.h"
 
 #include <iostream>
 
@@ -248,8 +249,6 @@ private:
 
 // Function prototypes
 // ****************************************************************************************
-int checkFileCrc( const char* );
-
 typedef union edc_data_t
 {
     int16_t data_int16;
@@ -1170,31 +1169,6 @@ edcuInitialize( const std::string& mbuf_name,
     return EdcuClock( sync_source, delay_ms );
 }
 
-/// Common routine to check file CRC.
-///	@param[in] *fName	Name of file to check.
-///	@return File CRC or -1 if file not found.
-int
-checkFileCrc( const char* fName )
-{
-    char        buffer[ 256 ];
-    FILE*       pipePtr;
-    struct stat statBuf;
-    long        chkSum = -99999;
-    strcpy( buffer, "cksum " );
-    strcat( buffer, fName );
-    if ( !stat( fName, &statBuf ) )
-    {
-        if ( ( pipePtr = popen( buffer, "r" ) ) != NULL )
-        {
-            fgets( buffer, 256, pipePtr );
-            pclose( pipePtr );
-            sscanf( buffer, "%ld", &chkSum );
-        }
-        return ( chkSum );
-    }
-    return ( -1 );
-}
-
 std::pair< std::string, int >
 parse_address( const std::string& str )
 {
diff --git a/src/epics/seq/test/daqdrc_standalone_edc_live_test b/src/epics/edcu/test/daqdrc_standalone_edc_live_test
similarity index 100%
rename from src/epics/seq/test/daqdrc_standalone_edc_live_test
rename to src/epics/edcu/test/daqdrc_standalone_edc_live_test
diff --git a/src/epics/seq/test/epics_test.py b/src/epics/edcu/test/epics_test.py
similarity index 100%
rename from src/epics/seq/test/epics_test.py
rename to src/epics/edcu/test/epics_test.py
diff --git a/src/epics/seq/test/test_standalone_edc_live_nds.sh.in b/src/epics/edcu/test/test_standalone_edc_live_nds.sh.in
similarity index 100%
rename from src/epics/seq/test/test_standalone_edc_live_nds.sh.in
rename to src/epics/edcu/test/test_standalone_edc_live_nds.sh.in
diff --git a/src/epics/seq/CMakeLists.txt b/src/epics/seq/CMakeLists.txt
index 243116de634291c6d8233b49f7dd8f616181ef75..3c9e1226b484e065e7b4226242dfadb56953c4b9 100644
--- a/src/epics/seq/CMakeLists.txt
+++ b/src/epics/seq/CMakeLists.txt
@@ -1,47 +1,21 @@
-if (Boost_FOUND)
+add_executable(test_sequencer_unit_tests
+        test/test_main.cc
+        test/test_simple_range.cc
+        test/test_fixed_size_string.cc
+        test/test_fixed_size_vector.cc
+        )
+target_include_directories(test_sequencer_unit_tests PUBLIC
+        ${CMAKE_CURRENT_SOURCE_DIR})
+target_link_libraries(test_sequencer_unit_tests PUBLIC
+    catch2)
 
-add_executable(standalone_edc
-        standalone_edcu.cc
-        ${CMAKE_CURRENT_SOURCE_DIR}/../../drv/rfm.c)
-#target_compile_options(standalone_edc PRIVATE -fsanitize=address)
-#target_link_libraries(standalone_edc PRIVATE asan)
-#target_compile_options(standalone_edc PRIVATE
-#        -fstack-protector -fstack-protector-strong)
-target_include_directories(standalone_edc PUBLIC
-        "${CMAKE_CURRENT_SOURCE_DIR}/../../include"
-        "${CMAKE_CURRENT_SOURCE_DIR}/../../include/drv"
-        ${Boost_INCLUDE_DIRS})
-target_link_libraries(standalone_edc PUBLIC
-        args
-        epics::ca
-        driver::ini_parsing
-        pv::simple_pv
-        ${Boost_LIBRARIES}
-        ${CMAKE_THREAD_LIBS_INIT}
-        driver::gpsclock)
-target_requires_cpp11(standalone_edc PUBLIC)
-
-configure_file(test/epics_test.py ${CMAKE_CURRENT_BINARY_DIR}/epics_test.py COPYONLY)
-configure_file(test/daqdrc_standalone_edc_live_test ${CMAKE_CURRENT_BINARY_DIR}/daqdrc_standalone_edc_live_test COPYONLY)
-configure_file(test/test_standalone_edc_live_nds.sh.in ${CMAKE_CURRENT_BINARY_DIR}/test_standalone_edc_live_nds.sh @ONLY)
-
-add_test(NAME test_standalone_edc_live_nds
-        COMMAND /bin/bash ./test_standalone_edc_live_nds.sh
-        WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
-
-install(TARGETS standalone_edc DESTINATION bin)
-
-else(Boost_FOUND)
-    message(WARNING "The standalone_edc will not be build as boost was not found")
-endif (Boost_FOUND)
+add_executable(casdf_seq
+        main.cc
+        sdf_file_loaded.c)
+target_link_libraries(casdf_seq PUBLIC ini_parsing shmem epics::seq epics::ca)
+target_compile_definitions(casdf_seq PUBLIC "-DCA_SDF=1")
 
 add_executable(modelsdf_seq
-        main.c
+        main.cc
         sdf_file_loaded.c)
 target_link_libraries(modelsdf_seq PUBLIC ini_parsing shmem epics::seq epics::ca)
-
-add_executable(casdf_seq
-        main.c
-        sdf_file_loaded.c)
-target_link_libraries(casdf_seq PUBLIC ini_parsing shmem epics::seq epics::ca)
-target_compile_definitions(casdf_seq PUBLIC "-DCA_SDF=1")
\ No newline at end of file
diff --git a/src/epics/seq/Makefile b/src/epics/seq/Makefile
deleted file mode 100644
index e167994a60f861b583b2c579b7d0383aa61758d2..0000000000000000000000000000000000000000
--- a/src/epics/seq/Makefile
+++ /dev/null
@@ -1,3 +0,0 @@
-LIBFLAGS += -lezca
-
-edcu.o	edcu.c
diff --git a/src/epics/seq/edcu.c b/src/epics/seq/edcu.c
deleted file mode 100644
index 49c1598f5dcc26baa47d91c53a12c76c39eb04cc..0000000000000000000000000000000000000000
--- a/src/epics/seq/edcu.c
+++ /dev/null
@@ -1,951 +0,0 @@
-///	@file /src/epics/seq/edcu.c
-///	@brief Contains required 'main' function to startup EPICS sequencers, along with supporting routines. 
-///<		This code is taken from EPICS example included in the EPICS distribution and modified for LIGO use.
-
-/********************COPYRIGHT NOTIFICATION**********************************
-This software was developed under a United States Government license
-described on the COPYRIGHT_UniversityOfChicago file included as part
-of this distribution.
-****************************************************************************/
-
-// TODO:
-// - Make appropriate log file entries
-// - Get rid of need to build skeleton.st
-
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <unistd.h>
-#include <time.h>
-#include <sys/stat.h>
-#include <sys/ioctl.h>
-#include <fcntl.h>
-
-#include "iocsh.h"
-#include "dbStaticLib.h"
-#include "crc.h"
-#include "dbCommon.h"
-#include "recSup.h"
-#include "dbDefs.h"
-#include "dbFldTypes.h"
-#include "dbAccess.h"
-
-#define epicsExportSharedSymbols
-#include "asLib.h"
-#include "asCa.h"
-#include "asDbLib.h"
-
-#include <daqmap.h>
-#include <param.h>
-// #include "fm10Gen.h"
-#include "findSharedMemory.h"
-#include "cadef.h"
-#include "fb.h"
-#include "../../drv/gpstime/gpstime.h"
-
-#include <pthread.h>
-
-#define EDCU_MAX_CHANS	50000
-// Gloabl variables		****************************************************************************************
-char timechannel[256];		///< Name of the GPS time channel for timestamping.
-char reloadtimechannel[256];	///< Name of EPICS channel which contains the BURT reload requests.
-struct timespec t;
-char logfilename[128];
-char edculogfilename[128];
-unsigned char naughtyList[EDCU_MAX_CHANS][64];
-
-// Function prototypes		****************************************************************************************
-int checkFileCrc(char *);
-void getSdfTime(char *);
-void logFileEntry(char *);
-void* check_crc(void* arg);
-
-unsigned long daqFileCrc;
-typedef struct daqd_c {
-	int num_chans;
-	int con_chans;
-	int val_events;
-	int con_events;
-	float channel_value[EDCU_MAX_CHANS];
-	char channel_name[EDCU_MAX_CHANS][64];
-	int channel_status[EDCU_MAX_CHANS];
-	long gpsTime;
-	long epicsSync;
-} daqd_c;
-
-typedef struct check_crc_params {
-    char *ini_filename;
-    int   orig_crc;
-    dbAddr message_dest;
-} check_crc_params;
-
-int num_chans_index = -1;
-int con_chans_index = -1;
-int nocon_chans_index = -1;
-int internal_channel_count = 0;
-pthread_t check_crc_thread;
-
-daqd_c daqd_edcu1;
-static struct rmIpcStr *dipc;
-static struct rmIpcStr *sipc;
-static char *shmDataPtr;
-static struct cdsDaqNetGdsTpNum *shmTpTable;
-static const int buf_size = DAQ_DCU_BLOCK_SIZE;
-static const int header_size = sizeof(struct rmIpcStr) + sizeof(struct cdsDaqNetGdsTpNum);
-static DAQ_XFER_INFO xferInfo;
-static float dataBuffer[2][EDCU_MAX_CHANS];
-static int timeIndex;
-static int cycleIndex;
-static int symmetricom_fd = -1;
-int timemarks[16] = {1000,63500,126000,188500,251000,313500,376000,438500,501000,563500,626000,688500,751000,813500,876000,938500};
-int nextTrig = 0;
-
-
-// End Header ************************************************************
-//
-
-// **************************************************************************
-/// Get current GPS time from the symmetricom IRIG-B card
-unsigned long symm_gps_time(unsigned long *frac, int *stt) {
-// **************************************************************************
-    unsigned long t[3];
-    ioctl (symmetricom_fd, IOCTL_SYMMETRICOM_TIME, &t);
-    t[1] *= 1000;
-    t[1] += t[2];
-    if (frac) *frac = t[1];
-         if (stt) *stt = 0;
-         // return  t[0] + daqd.symm_gps_offset;
-         return  t[0];
-}
-// **************************************************************************
-void waitGpsTrigger(unsigned long gpssec, int cycle)
-// No longer used in favor of sync to IOP
-// **************************************************************************
-{
-unsigned long gpsSec, gpsuSec;
-int gpsx;
-	do{
-		usleep(1000);
-		gpsSec = symm_gps_time(&gpsuSec, &gpsx);
-		gpsuSec /= 1000;
-	}while(gpsSec < gpssec || gpsuSec < timemarks[cycle]); 
-}
-
-// **************************************************************************
-long waitNewCycle(long *gps_sec)
-// **************************************************************************
-{
-  static long newCycle = 0;
-  static int lastCycle = 0;
-  static int sync21pps = 1;
-  unsigned long lastSec;
-
-    if(sync21pps)  {
-        for (;sipc->cycle;) usleep(1000); 
-        printf("Found Sync at %ld %ld\n",sipc->bp[lastCycle].timeSec, sipc->bp[lastCycle].timeNSec);
-        sync21pps = 0;
-    }
-    do {
-        usleep(1000);
-        newCycle = sipc->cycle;
-    }while (newCycle == lastCycle);
-    *gps_sec = sipc->bp[newCycle].timeSec;
-    // printf("new cycle %d %ld\n",newCycle,*gps_sec);
-    lastCycle = newCycle;
-    return newCycle;
-}
-
-// **************************************************************************
-/// See if the GPS card is locked.
-int symm_gps_ok() {
-// **************************************************************************
-    unsigned long req = 0;
-    ioctl (symmetricom_fd, IOCTL_SYMMETRICOM_STATUS, &req);
-    printf("Symmetricom status: %s\n", req? "LOCKED": "UNCLOCKED");
-   return req;
-}
-
-// **************************************************************************
-unsigned long symm_initialize()
-// **************************************************************************
-{
-	symmetricom_fd =  open ("/dev/gpstime", O_RDWR | O_SYNC);
-	if (symmetricom_fd < 0) {
-	       perror("/dev/gpstime");
-	       exit(1);
-	}
-	unsigned long gpsSec, gpsuSec;
-	int gpsx;
-	int gpssync;
-	gpssync =  symm_gps_ok();
-	gpsSec = symm_gps_time(&gpsuSec, &gpsx);
-	printf("GPS SYNC = %d %d\n",gpssync,gpsx);
-	printf("GPS SEC = %ld  USEC = %ld  OTHER = %d\n",gpsSec,gpsuSec,gpsx);
-	// Set system to start 2 sec from now.
-	gpsSec += 2;
-	return(gpsSec);
-}
-
-// **************************************************************************
-void connectCallback(struct connection_handler_args args) {
-// **************************************************************************
-        unsigned long chnum = (unsigned long)ca_puser(args.chid);
-	daqd_edcu1.channel_status[chnum] = args.op == CA_OP_CONN_UP? 0: 0xbad;
-        if (args.op == CA_OP_CONN_UP) daqd_edcu1.con_chans++; else daqd_edcu1.con_chans--;
-        daqd_edcu1.con_events++;
-}
-
-
-// **************************************************************************
-void subscriptionHandler(struct event_handler_args args) {
-// **************************************************************************
- 	daqd_edcu1.val_events++;
-        if (args.status != ECA_NORMAL) {
-        	return;
-        }
-        if (args.type == DBR_FLOAT) {
-        	float val = *((float *)args.dbr);
-                daqd_edcu1.channel_value[(unsigned long)args.usr] = val;
-        }else{
-		printf("Arg type unknown\n");
-	}
-}
-
-// **************************************************************************
-int edcuClearSdf(char *pref)
-// **************************************************************************
-{
-unsigned char clearString[64] = "          ";
-int flength = 62;
-long status;
-dbAddr saddr;
-int ii;
-char s[64];
-char s1[64];
-char s2[64];
-char s3[64];
-char s4[64];
-dbAddr baddr;
-dbAddr maddr;
-dbAddr taddr;
-dbAddr daddr;
-
-
-
-	for(ii=0;ii<40;ii++)
-        {
-	 sprintf(s, "%s_%s_STAT%d", pref,"SDF_SP", ii);
-	 status = dbNameToAddr(s,&saddr);
-	 status = dbPutField(&saddr,DBR_UCHAR,clearString,16);
-
-	 sprintf(s1, "%s_%s_STAT%d_BURT", pref,"SDF_SP", ii);
-	 status = dbNameToAddr(s1,&baddr);
-	 status = dbPutField(&baddr,DBR_UCHAR,clearString,flength);
-
-	 sprintf(s2, "%s_%s_STAT%d_LIVE", pref,"SDF_SP", ii);
-	 status = dbNameToAddr(s2,&maddr);
-	 status = dbPutField(&maddr,DBR_UCHAR,clearString,flength);
-
-	 sprintf(s3, "%s_%s_STAT%d_TIME", pref,"SDF_SP", ii);
-	 status = dbNameToAddr(s3,&taddr);
-	 status = dbPutField(&taddr,DBR_UCHAR,clearString,flength);
-
-	  sprintf(s4, "%s_%s_STAT%d_DIFF", pref,"SDF_SP", ii);
-	  status = dbNameToAddr(s4,&daddr);
-	  status = dbPutField(&daddr,DBR_UCHAR,clearString,flength);
-
-	 }
-
-
-}
-
-// **************************************************************************
-int edcuFindUnconnChannels()
-// **************************************************************************
-{
-int ii;
-int dcc = 0;
-
-	for (ii=0;ii<daqd_edcu1.num_chans;ii++)
-	{
-		if(daqd_edcu1.channel_status[ii] != 0)
-		{
-			sprintf(naughtyList[dcc],"%s",daqd_edcu1.channel_name[ii]);
-			dcc ++;
-		}
-	}
-	return(dcc);
-}
-// **************************************************************************
-int edcuReportUnconnChannels(char *pref, int dc, int offset)
-// **************************************************************************
-{
-int ii;
-dbAddr saddr;
-dbAddr laddr;
-dbAddr sperroraddr;
-char s[64];
-char sl[64];
-long status;
-int flength = 62;
-unsigned char tmpstr[64];
-int rc = 0;
-int myindex = 0;
-int numDisp = 0;
-int lineNum = 0;
-int erucError = 0;
-
-
-	myindex = offset * 40;
-	if(myindex > dc) {
-		myindex = 0;
-		erucError = -1;
-	}
-	rc = myindex + 40;
-	if(rc > dc) rc = dc;
-	// printf("Naught =  %d to %d\n",myindex,rc);
-	// printf("In error listing \n");
-	for (ii=myindex;ii<rc;ii++)
-	{
-		sprintf(s, "%s_%s_STAT%d", pref,"SDF_SP", (ii - myindex));
-		status = dbNameToAddr(s,&saddr);
-		if(status) 
-		{
-			printf("Can't connect to %s\n",s);
-		} else {
-			sprintf(tmpstr,"%s",naughtyList[ii]);
-			status = dbPutField(&saddr,DBR_UCHAR,tmpstr,flength);
-			numDisp ++;
-		}
-		sprintf(sl, "%s_SDF_LINE_%d", pref, (ii - myindex));
-		status = dbNameToAddr(sl,&laddr);
-		if(status) 
-		{
-			printf("Can't connect to %s\n",s);
-		} else {
-			lineNum = ii + 1;
-			status = dbPutField(&laddr,DBR_LONG,&lineNum,1);
-		}
-	}
-	// Clear out remaining reporting channels.
-	sprintf(tmpstr,"%s","  ");
-	for (ii=numDisp;ii<40;ii++) {
-		sprintf(s, "%s_%s_STAT%d", pref,"SDF_SP", ii);
-		status = dbNameToAddr(s,&saddr);
-		status = dbPutField(&saddr,DBR_UCHAR,tmpstr,flength);
-		// sprintf(sl, "%s_SDF_LINE_%d", pref, (ii - myindex));
-		sprintf(sl, "%s_SDF_LINE_%d", pref, ii);
-		status = dbNameToAddr(sl,&laddr);
-		lineNum += 1;
-		status = dbPutField(&laddr,DBR_LONG,&lineNum,1);
-	}
-	 char speStat[256]; sprintf(speStat, "%s_%s", pref, "SDF_TABLE_ENTRIES");             // Setpoint diff counter
-	 status = dbNameToAddr(speStat,&sperroraddr);                    // Get Address
-	 status = dbPutField(&sperroraddr,DBR_LONG,&dc,1);          // Init to zero.
-
-	return(erucError);
-}
-
-/**
- * Scan the input text for the first non-whitespace character and return a pointer to that location.
- * @param line NULL terminated string to check.
- * @return Pointer to the the first non whitespace (space, tab, nl, cr) character.  Returns NULL iff
- * line is NULL.
- */
-const char* skip_whitespace(const char* line)
-{
-    const char* cur = line;
-    char ch = 0;
-    if (!line)
-    {
-        return NULL;
-    }
-    ch = *cur;
-    while (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n')
-    {
-        ++cur;
-        ch = *cur;
-    }
-    return cur;
-}
-
-/**
- * Given a line with an comment denoted by '#' terminate
- * the line at the start of the comment.
- * @param line The line to modify, a NULL terminated string
- * @note This may modify the string pointed to by line.
- * This is safe to call with a NULL pointer.
- */
-void remove_line_comments(char *line)
-{
-    char ch = 0;
-
-    if (!line)
-    {
-        return;
-    }
-    while ((ch = *line))
-    {
-        if (ch == '#')
-        {
-            *line = '\0';
-            return;
-        }
-        ++line;
-    }
-}
-
-/**
- * Given a NULL terminated string remove any trailing whitespace
- * @param line The line to modify, a NULL terminated string
- * @note This may modify the string pointed to by line.
- * This is safe to call with a NULL pointer.
- */
-void remove_trailing_whitespace(char* newname)
-{
-    char* cur = newname;
-    char* last_non_ws = NULL;
-    char ch = 0;
-
-    if (!newname)
-    {
-        return;
-    }
-    ch = *cur;
-    while (ch)
-    {
-        if (ch != ' ' && ch != '\t' && ch != '\r' && ch != '\n')
-        {
-            last_non_ws = cur;
-        }
-        ++cur;
-        ch = *cur;
-    }
-    if (!last_non_ws)
-    {
-        *newname = '\0';
-    }
-    else
-    {
-        last_non_ws++;
-        *last_non_ws = '\0';
-    }
-}
-
-// **************************************************************************
-void edcuCreateChanFile(char *fdir, char *edcuinifilename, char *fecid) {
-// **************************************************************************
-    int ok = 0;
-    int i = 0;
-    int status = 0;
-    char errMsg[64] = "";
-    FILE *daqfileptr = NULL;
-    FILE *edcuini = NULL;
-    FILE *edcumaster = NULL;
-    char masterfile[64] = "";
-    char edcuheaderfilename[64] = "";
-    char line[128] = "";
-    char *newname = 0;
-    char edcufilename[64] = "";
-    char *dcuid = 0;
-
-
-	sprintf(errMsg,"%s",fecid);
-	dcuid = strtok(errMsg,"-");
-	dcuid = strtok(NULL,"-");
-	sprintf(masterfile, "%s%s", fdir, "edcumaster.txt");
-	sprintf(edcuheaderfilename, "%s%s", fdir, "edcuheader.txt");
-
-	// Open the master file which contains list of EDCU files to read channels from.
-	edcumaster = fopen(masterfile,"r");
-	if(edcumaster == NULL) {
-		sprintf(errMsg,"DAQ FILE ERROR: FILE %s DOES NOT EXIST\n",masterfile);
-        logFileEntry(errMsg);
-        goto done;
-    }
-	// Open the file to write the composite channel list.
-	edcuini = fopen(edcuinifilename,"w");
-	if(edcuini == NULL) {
-		sprintf(errMsg,"DAQ FILE ERROR: FILE %s DOES NOT EXIST\n",edcuinifilename);
-        logFileEntry(errMsg);
-        goto done;
-    }
-
-	// Write standard header into .ini file
-	fprintf(edcuini,"%s","[default] \n");
-	fprintf(edcuini,"%s","gain=1.00 \n");
-	fprintf(edcuini,"%s","datatype=4 \n");
-	fprintf(edcuini,"%s","ifoid=0 \n");
-	fprintf(edcuini,"%s","slope=1 \n");
-	fprintf(edcuini,"%s","acquire=3 \n");
-	fprintf(edcuini,"%s","offset=0 \n");
-	fprintf(edcuini,"%s","units=undef \n");
-	fprintf(edcuini,"%s%s%s","dcuid=",dcuid," \n");
-	fprintf(edcuini,"%s","datarate=16 \n\n");
-
-	// Read the master file entries.
-	while(fgets(line,sizeof line,edcumaster) != NULL) {
-		newname = strtok(line,"\n");
-		if (!newname)
-        {
-		    continue;
-        }
-		newname = (char*)skip_whitespace(newname);
-        remove_line_comments(newname);
-        remove_trailing_whitespace(newname);
-
-		if (*newname == '\0')
-        {
-		    continue;
-        }
-		strcpy(edcufilename,fdir);
-		strcat(edcufilename,newname);
-		printf("File in master = %s\n",edcufilename);
-		daqfileptr = fopen(edcufilename,"r");
-		if(daqfileptr == NULL) {
-			sprintf(errMsg,"DAQ FILE ERROR: FILE %s DOES NOT EXIST OR CANNOT BE READ!\n",edcufilename);
-			logFileEntry(errMsg);
-			goto done;
-		}
-		while(fgets(line,sizeof line,daqfileptr) != NULL) {
-			fprintf(edcuini,"%s",line);
-		}
-		fclose(daqfileptr);
-		daqfileptr = NULL;
-	}
-	ok = 1;
-done:
-    if (daqfileptr) fclose(daqfileptr);
-	if (edcuini) fclose(edcuini);
-    if (edcumaster) fclose(edcumaster);
-    if (!ok)
-    {
-        exit(1);
-    }
-}
-
-// **************************************************************************
-void edcuCreateChanList(char *daqfilename) {
-// **************************************************************************
-int i;
-int status;
-FILE *daqfileptr;
-FILE *edculog;
-char errMsg[64];
-// char daqfile[64];
-char line[128];
-char *newname;
-
-
-char *pref = getenv("PREFIX");
-char eccname[256]; sprintf(eccname, "%s_%s", pref, "EDCU_CHAN_CONN");
-char chcntname[256]; sprintf(chcntname, "%s_%s", pref, "EDCU_CHAN_CNT");
-char cnfname[256]; sprintf(cnfname, "%s_%s", pref, "EDCU_CHAN_NOCON"); 
-
-
-	// sprintf(daqfile, "%s%s", fdir, "EDCU.ini");
-	daqd_edcu1.num_chans = 0;
-	daqfileptr = fopen(daqfilename,"r");
-	if(daqfileptr == NULL) {
-		sprintf(errMsg,"DAQ FILE ERROR: FILE %s DOES NOT EXIST\n",daqfilename);
-	        logFileEntry(errMsg);
-        }
-	edculog = fopen(edculogfilename,"w");
-	if(daqfileptr == NULL) {
-		sprintf(errMsg,"DAQ FILE ERROR: FILE %s DOES NOT EXIST\n",edculogfilename);
-	        logFileEntry(errMsg);
-        }
-	while(fgets(line,sizeof line,daqfileptr) != NULL) {
-		fprintf(edculog,"%s",line);
-		status = strlen(line);
-		if(strncmp(line,"[",1) == 0 && status > 0) {
-			newname = strtok(line,"]");
-			// printf("status = %d New name = %s and %s\n",status,line,newname);
-			newname = strtok(line,"[");
-			// printf("status = %d New name = %s and %s\n",status,line,newname);
-			if(strcmp(newname,"default") == 0) {
-				printf("DEFAULT channel = %s\n", newname);
-			} else {
-				// printf("NEW channel = %s\n", newname);
-				sprintf(daqd_edcu1.channel_name[daqd_edcu1.num_chans],"%s",newname);
-				daqd_edcu1.num_chans ++;
-			}
-		}
-	}
-	fclose(daqfileptr);
-	fclose(edculog);
-
-	xferInfo.crcLength = 4 * daqd_edcu1.num_chans;
-	printf("CRC data length = %d\n",xferInfo.crcLength);
-
-	     chid chid1;
-	ca_context_create(ca_enable_preemptive_callback);
-
-
-     	for (i = 0; i < daqd_edcu1.num_chans; i++) {
-            if (strcmp(daqd_edcu1.channel_name[i], chcntname) == 0) {
-                num_chans_index = i;
-                internal_channel_count = internal_channel_count + 1;
-                daqd_edcu1.channel_status[i] = 0;
-            }
-            else if (strcmp(daqd_edcu1.channel_name[i], eccname) == 0) {
-                con_chans_index = i;
-                internal_channel_count = internal_channel_count + 1;
-                daqd_edcu1.channel_status[i] = 0;
-            }
-            else if (strcmp(daqd_edcu1.channel_name[i], cnfname) == 0) {
-                nocon_chans_index = i;
-                internal_channel_count = internal_channel_count + 1;
-                daqd_edcu1.channel_status[i] = 0;
-            }
-            else {
-        	     status = ca_create_channel(daqd_edcu1.channel_name[i], connectCallback, (void *)i, 0, &chid1);
-	             status = ca_create_subscription(DBR_FLOAT, 0, chid1, DBE_VALUE,
-		    	     subscriptionHandler, (void *)i, 0);
-            }
-    	}
-
-    daqd_edcu1.con_chans = daqd_edcu1.con_chans + internal_channel_count;
-
-	timeIndex = 0;
-}
-
-// **************************************************************************
-void edcuWriteData(int daqBlockNum, unsigned long cycle_gps_time, int dcuId, int daqreset)
-// **************************************************************************
-{
-float *daqData;
-int buf_size;
-int ii;
-
-
-
-    if (num_chans_index != -1) {
-        daqd_edcu1.channel_value[num_chans_index] = daqd_edcu1.num_chans;
-    }
-
-    if (con_chans_index != -1) {
-        daqd_edcu1.channel_value[con_chans_index] = daqd_edcu1.con_chans;
-    }
-
-    if (nocon_chans_index != -1) {
-        daqd_edcu1.channel_value[nocon_chans_index] = daqd_edcu1.num_chans - daqd_edcu1.con_chans;
-    }
-
-
-
-		
-	buf_size = DAQ_DCU_BLOCK_SIZE*DAQ_NUM_SWING_BUFFERS;
-	daqData = (float *)(shmDataPtr + (buf_size * daqBlockNum));
-    memcpy(daqData, daqd_edcu1.channel_value, daqd_edcu1.num_chans * sizeof(float));
-	dipc->dcuId = dcuId;
-	dipc->crc = daqFileCrc;
-	dipc->dataBlockSize = xferInfo.crcLength;
-	dipc->bp[daqBlockNum].cycle = daqBlockNum;
-	dipc->bp[daqBlockNum].crc = xferInfo.crcLength;
-	dipc->bp[daqBlockNum].timeSec = (unsigned int) cycle_gps_time;
-	dipc->bp[daqBlockNum].timeNSec = (unsigned int)daqBlockNum;
-    if(daqreset) {
-        shmTpTable->count = 1;
-        shmTpTable->tpNum[0] = daqreset;
-    } else {
-        shmTpTable->count = 0;
-        shmTpTable->tpNum[0] = 0;
-    }
-	dipc->cycle = daqBlockNum;	// Triggers sending of data by mx_stream.
-
-}
-
-// **************************************************************************
-void edcuInitialize(char *shmem_fname, char *sync_source)
-// **************************************************************************
-{
-
-	// Find start of DAQ shared memory
-	void *dcu_addr = findSharedMemory(shmem_fname);
-	// Find the IPC area to communicate with mxstream
-	dipc = (struct rmIpcStr *)((char *)dcu_addr + CDS_DAQ_NET_IPC_OFFSET);
-	// Find the DAQ data area.
-    shmDataPtr = (char *)((char *)dcu_addr + CDS_DAQ_NET_DATA_OFFSET);
-    shmTpTable = (struct cdsDaqNetGdsTpNum *)((char *)dcu_addr + CDS_DAQ_NET_GDS_TP_TABLE_OFFSET);
-    // Find Sync source
-	void *sync_addr = findSharedMemory(sync_source);
-	sipc = (struct rmIpcStr *)((char *)sync_addr + CDS_DAQ_NET_IPC_OFFSET);
-}
-
-
-
-/// Common routine to check file CRC.
-///	@param[in] *fName	Name of file to check.
-///	@return File CRC or -1 if file not found.
-int checkFileCrc(char *fName)
-{
-char buffer[256];
-FILE *pipePtr;
-struct stat statBuf;
-long chkSum = -99999;
-      	strcpy(buffer, "cksum "); 
-      	strcat(buffer, fName); 
-      	if (!stat(fName, &statBuf) ) { 
-         	if ((pipePtr = popen(buffer, "r")) != NULL) {
-            	fgets(buffer, 256, pipePtr);
-            	pclose(pipePtr); 
-            	sscanf(buffer, "%ld", &chkSum);
-         	} 
-		return(chkSum);
-    	}    
-	return(-1);
-}
-
-/// Routine for reading GPS time from model EPICS record.
-///	@param[out] timestring 	Pointer to char string in which GPS time is to be written.
-void getSdfTime(char *timestring)
-{
-
-	dbAddr paddr;
-	long ropts = 0;
-	long nvals = 1;
-	long status;
-
-	status = dbNameToAddr(timechannel,&paddr);
-	status = dbGetField(&paddr,DBR_STRING,timestring,&ropts,&nvals,NULL);
-}
-
-/// Routine for logging messages to ioc.log file.
-/// 	@param[in] message Ptr to string containing message to be logged.
-void logFileEntry(char *message)
-{
-	FILE *log;
-	char timestring[256];
-	long status;
-	dbAddr paddr;
-
-	getSdfTime(timestring);
-	log = fopen(logfilename,"a");
-	if(log == NULL) {
-		status = dbNameToAddr(reloadtimechannel,&paddr);
-		status = dbPutField(&paddr,DBR_STRING,"ERR - NO LOG FILE FOUND",1);
-	} else {
-		fprintf(log,"%s\n%s\n",timestring,message);
-		fprintf(log,"***************************************************\n");
-		fclose(log);
-	}
-
-}
-
-/// Called on EPICS startup; This is generic EPICS provided function, modified for LIGO use.
-int main(int argc,char *argv[])
-{
-	// Addresses for SDF EPICS records.
-	// Initialize request for file load on startup.
-	long status;
-	int request;
-	int daqTrigger;
-	long ropts = 0;
-	long nvals = 1;
-	int rdstatus = 0;
-	char timestring[128];
-	int ii;
-	long coeffFileCrc;
-	char modfilemsg[] = "Modified File Detected ";
-	struct stat st = {0};
-	char filemsg[128];
-	char logmsg[256];
-	int pageNum = 0;
-	int pageNumDisp = 0;
-    int daqreset = 0;
-    char errMsg[64];
-    char *dcuid;
-    int send_daq_reset = 0;
-
-    check_crc_params crc_params;
-
-    if(argc>=2) {
-        iocsh(argv[1]);
-
-    memset(&crc_params, 0, sizeof(crc_params));
-
-	// printf("Executing post script commands\n");
-	// Get environment variables from startup command to formulate EPICS record names.
-	char *pref = getenv("PREFIX");
-	char *modelname =  getenv("SDF_MODEL");
-	char daqsharedmemname[64];
-	sprintf(daqsharedmemname, "%s%s", modelname, "_daq");
-	char *sync =  getenv("SYNC_SRC");
-	char syncsharedmemname[64];
-	sprintf(syncsharedmemname, "%s%s", sync, "_daq");
-	char *targetdir =  getenv("TARGET_DIR");
-	char *daqFile =  getenv("DAQ_FILE");
-	char *daqDir =  getenv("DAQ_DIR");
-	char *coeffFile =  getenv("COEFF_FILE");
-	char *logdir = getenv("LOG_DIR");
-	if(stat(logdir, &st) == -1) mkdir(logdir,0777);
-	sprintf(errMsg,"%s",pref);
-	dcuid = strtok(errMsg,"-");
-	dcuid = strtok(NULL,"-");
-    int mydcuid = atoi(dcuid);
-	// strcat(sdf,"_safe");
-	printf("My prefix is %s\n",pref);
-	printf("My dcuid is %d\n",mydcuid);
-	sprintf(logfilename, "%s%s", logdir, "/ioc.log");
-	printf("LOG FILE = %s\n",logfilename);
-sleep(2);
-	// **********************************************
-	//
-	dbAddr eccaddr;
-	char eccname[256]; sprintf(eccname, "%s_%s", pref, "EDCU_CHAN_CONN");			// Number of setting channels in EPICS db
-	status = dbNameToAddr(eccname,&eccaddr);
-
-	dbAddr chcntaddr;
-	// char chcntname[256]; sprintf(chcntname, "%s", "X1:DAQ-FEC_54_EPICS_CHAN_CNT");	// Request to monitor all channels.
-	char chcntname[256]; sprintf(chcntname, "%s_%s", pref, "EDCU_CHAN_CNT");	// Request to monitor all channels.
-	status = dbNameToAddr(chcntname,&chcntaddr);		// Get Address.
-
-	dbAddr chnotfoundaddr;
-	char cnfname[256]; sprintf(cnfname, "%s_%s", pref, "EDCU_CHAN_NOCON");		// Number of channels not found.
-	status = dbNameToAddr(cnfname,&chnotfoundaddr);
-
-	dbAddr daqbyteaddr;
-	char daqbytename[256]; sprintf(daqbytename, "%s_%s", pref, "DAQ_BYTE_COUNT");	// Request to monitor all channels.
-	status = dbNameToAddr(daqbytename,&daqbyteaddr);		// Get Address.
-
-	char moddaqfilemsg[256]; sprintf(moddaqfilemsg, "%s_%s", pref, "MSGDAQ");	// Record to write if DAQ file changed.
-	status = dbNameToAddr(moddaqfilemsg, &(crc_params.message_dest));
-
-	sprintf(timechannel,"%s_%s", pref, "TIME_STRING");
-	// printf("timechannel = %s\n",timechannel);
-	
-	dbAddr reloadtimeaddr;
-	sprintf(reloadtimechannel,"%s_%s", pref, "MSGDAQ");			// Time of last BURT reload
-	status = dbNameToAddr(reloadtimechannel,&reloadtimeaddr);
-
-	getSdfTime(timestring);
-	status = dbPutField(&reloadtimeaddr,DBR_STRING,timestring,1);
-
-	dbAddr gpstimedisplayaddr;
-	char gpstimedisplayname[256]; sprintf(gpstimedisplayname, "%s_%s", pref, "TIME_DIAG");	// SDF Save command.
-	status = dbNameToAddr(gpstimedisplayname,&gpstimedisplayaddr);		// Get Address.
-
-	dbAddr pagereqaddr;
-	char pagereqname[256]; sprintf(pagereqname, "%s_%s", pref, "SDF_PAGE");	// SDF Page request.
-	status = dbNameToAddr(pagereqname,&pagereqaddr);		// Get Address.
-
-	dbAddr daqresetaddr;
-	char daqresetname[256]; sprintf(daqresetname, "%s_%s", pref, "EDCU_DAQ_RESET");	// SDF Page request.
-	status = dbNameToAddr(daqresetname,&daqresetaddr);		// Get Address.
-
-// EDCU STUFF ********************************************************************************************************
-	
-	sprintf(edculogfilename, "%s%s", logdir, "/edcu.log");
-	for (ii=0;ii<EDCU_MAX_CHANS;ii++) daqd_edcu1.channel_status[ii] = 0xbad;
-	edcuInitialize(daqsharedmemname,syncsharedmemname);
-	edcuCreateChanFile(daqDir,daqFile,pref);
-	edcuCreateChanList(daqFile);
-	status = dbPutField(&chcntaddr,DBR_LONG,&daqd_edcu1.num_chans,1);
-	int datarate = daqd_edcu1.num_chans * 64 / 1000;
-	status = dbPutField(&daqbyteaddr,DBR_LONG,&datarate,1);
-
-// Start SPECT
-	daqd_edcu1.gpsTime = symm_initialize();
-	daqd_edcu1.epicsSync = 0;
-
-// End SPECT
-
-	int dropout = 0;
-	int numDC = 0;
-	int cycle = 0;
-	int numReport = 0;
-
-	// Initialize DAQ and COEFF file CRC checksums for later compares.
-	daqFileCrc = checkFileCrc(daqFile);
-	printf("DAQ file CRC = %u \n",daqFileCrc);  
-	coeffFileCrc = checkFileCrc(coeffFile);
-	sprintf(logmsg,"%s\n%s = %u\n%s = %d","EDCU code restart","File CRC",daqFileCrc,"Chan Cnt",daqd_edcu1.num_chans);
-	logFileEntry(logmsg);
-	edcuClearSdf(pref);
-
-    crc_params.ini_filename = daqFile;
-    crc_params.orig_crc = daqFileCrc;
-
-    pthread_create(&check_crc_thread, NULL, check_crc, &crc_params);
-	// Start Infinite Loop 		*******************************************************************************
-	for(;;) {
-		dropout = 0;
-        daqd_edcu1.epicsSync = waitNewCycle(&daqd_edcu1.gpsTime);
-		edcuWriteData(daqd_edcu1.epicsSync, daqd_edcu1.gpsTime,mydcuid,send_daq_reset);
-        send_daq_reset = 0;
-		status = dbPutField(&gpstimedisplayaddr,DBR_LONG,&daqd_edcu1.gpsTime,1);		// Init to zero.
-		status = dbPutField(&daqbyteaddr,DBR_LONG,&datarate,1);
-		int conChans = daqd_edcu1.con_chans;
-		status = dbPutField(&eccaddr,DBR_LONG,&conChans,1);
-        // Check unconnected channels once per second
-		if (daqd_edcu1.epicsSync == 0) {
-			status = dbGetField(&daqresetaddr,DBR_LONG,&daqreset,&ropts,&nvals,NULL);
-            if(daqreset) {
-                status = dbPutField(&daqresetaddr,DBR_LONG,&ropts,1);  // Init to zero.
-                send_daq_reset = daqreset;
-            }
-			status = dbGetField(&pagereqaddr,DBR_LONG,&pageNum,&ropts,&nvals,NULL);
-            if((int)pageNum != 0) {
-                pageNumDisp += pageNum;
-                if(pageNumDisp < 0) pageNumDisp = 0;
-                status = dbPutField(&pagereqaddr,DBR_LONG,&ropts,1);                // Init to zero.
-            }
-			numDC = edcuFindUnconnChannels();
-            if(numDC < (pageNumDisp * 40)) pageNumDisp --; 
-			numReport = edcuReportUnconnChannels(pref,numDC,pageNumDisp);
-		}
-		status = dbPutField(&chnotfoundaddr,DBR_LONG,&numDC,1);
-	}
-	sleep(0xfffffff);
-    } else
-    	iocsh(NULL);
-    return(0);
-}
-
-
-/**
- * @brief main loop for the crc checking thread.
- * @param arg Input structure defining the epics variables to set.
- * @return null
- * @note this is outside of the main thread so that it does not stall
- * the filling of the mbuf while waiting for filesystem I/O (ie crc check
- * of the file).
- */
-void* check_crc(void* arg) {
-    check_crc_params* crc_params = (check_crc_params*)arg;
-    char okfilemsg[] = "";
-    char modfilemsg[] = "Modified File Detected ";
-    int cur_crc = 0;
-    int was_ok = 1;
-
-
-    if (!crc_params)
-    {
-        logFileEntry("Unable to check the ini file");
-        return 0;
-    }
-
-    while (1) {
-        sleep(5);
-        cur_crc = checkFileCrc(crc_params->ini_filename);
-
-        if (cur_crc != crc_params->orig_crc) {
-            dbPutField(&(crc_params->message_dest),DBR_STRING,modfilemsg,1);
-            if (was_ok) {
-                logFileEntry("Detected Change to DAQ Config file.");
-            }
-            was_ok = 0;
-        } else {
-            dbPutField(&(crc_params->message_dest), DBR_STRING, okfilemsg, 1);
-            if (!was_ok) {
-                logFileEntry("DAQ config file restored to its expected state.");
-            }
-            was_ok = 1;
-        }
-    }
-    return 0;
-}
-
diff --git a/src/epics/seq/epics_channel.hh b/src/epics/seq/epics_channel.hh
new file mode 100644
index 0000000000000000000000000000000000000000..8074e0d378d116bbcf8ec95fb7c289111d8784c0
--- /dev/null
+++ b/src/epics/seq/epics_channel.hh
@@ -0,0 +1,125 @@
+//
+// Created by jonathan.hanks on 9/7/21.
+//
+
+#ifndef DAQD_TRUNK_EPICS_CHANNEL_HH
+#define DAQD_TRUNK_EPICS_CHANNEL_HH
+
+#include <cstdint>
+#include <type_traits>
+#include <stdexcept>
+
+#include "dbAccess.h"
+
+#include "epics_channel_common.hh"
+
+namespace epics
+{
+    namespace detail
+    {
+        template < PVType T >
+        struct epics_lookup
+        {
+        };
+
+        template <>
+        struct epics_lookup<PVType::UInt16>
+        {
+            static const auto value = DBF_USHORT;
+        };
+
+        template <>
+        struct epics_lookup<PVType::Int32>
+        {
+            static const auto value = DBF_LONG;
+        };
+
+        template <>
+        struct epics_lookup<PVType::UInt32>
+        {
+            static const auto value = DBF_ULONG;
+        };
+
+        template <>
+        struct epics_lookup<PVType::Float64>
+        {
+            static const auto value = DBF_DOUBLE;
+        };
+
+        template <>
+        struct epics_lookup<PVType::String>
+        {
+            static const auto value = DBF_STRING;
+        };
+    }
+
+    template<PVType DataType>
+    class DBEntry {
+        static constexpr const dbfType epicsType = detail::epics_lookup<DataType>::value;
+    public:
+        struct ValidateAddress {};
+
+        using value_type = typename type_lookup<DataType>::type;
+
+        explicit DBEntry(channel_name name)
+        {
+            dbNameToAddr(name.c_str(), &addr_);
+        }
+
+        DBEntry(channel_name name, ValidateAddress tag)
+        {
+            auto status = dbNameToAddr(name.c_str(), &addr_);
+            if ( status )
+            {
+                throw std::runtime_error("Failure with PV lookup in the EPICS "
+                                          "database. If this is the first PV "
+                                          "this usually signifies an error "
+                                          "with the .cmd passed to iocsh(), "
+                                          "or a misconfigured EPICS "
+                                          "environment");
+            }
+        }
+
+        DBEntry(channel_name name, const char* val)
+        {
+            static_assert(epicsType == DBF_STRING, "Initialization by const char* must be on a string type");
+            dbNameToAddr(name.c_str(), &addr_);
+            dbPutField(&addr_, epicsType, reinterpret_cast<const void*>(val), 1);
+        }
+
+
+        DBEntry(channel_name name, const value_type& val)
+        {
+            dbNameToAddr(name.c_str(), &addr_);
+            dbPutField(&addr_, epicsType, reinterpret_cast<const void*>(&val), 1);
+        }
+
+        void
+        set(const value_type& val)
+        {
+            dbPutField(&addr_, epicsType, reinterpret_cast<const void*>(&val), 1);
+        }
+
+        void
+        set(const char* val)
+        {
+            static_assert(epicsType == DBF_STRING, "Setting a const char* must be done on a string type");
+            dbPutField(&addr_, epicsType, reinterpret_cast<const void*>(val), 1);
+        }
+
+        void
+        get(value_type& val)
+        {
+            long ropts{0};
+            long nvals{1};
+            dbGetField(&addr_, epicsType, &val, &ropts, &nvals, nullptr);
+        }
+    private:
+
+
+        dbAddr addr_{};
+    };
+
+}
+
+#endif //DAQD_TRUNK_EPICS_CHANNEL_HH
diff --git a/src/epics/seq/epics_channel_common.hh b/src/epics/seq/epics_channel_common.hh
new file mode 100644
index 0000000000000000000000000000000000000000..2f3d3af8d04bdd2f6d571b9d6fe837070d1ebd96
--- /dev/null
+++ b/src/epics/seq/epics_channel_common.hh
@@ -0,0 +1,66 @@
+//
+// Created by jonathan.hanks on 3/21/22.
+//
+
+#ifndef DAQD_TRUNK_EPICS_CHANNEL_TYPES_HH
+#define DAQD_TRUNK_EPICS_CHANNEL_TYPES_HH
+
+#include "fixed_size_string.hh"
+
+namespace epics
+{
+    using channel_name = embedded::fixed_string<256>;
+
+    inline channel_name
+    make_name(const char* prefix, const char* name)
+    {
+        channel_name output{};
+        output.printf("%s_%s", prefix, name);
+        return output;
+    }
+
+    enum class PVType {
+        UInt16,
+        Int32,
+        UInt32,
+        Float64,
+        String,
+    };
+
+    template < PVType T >
+    struct type_lookup
+    {
+    };
+
+    template <>
+    struct type_lookup<PVType::UInt16>
+    {
+        using type=std::uint16_t;
+    };
+
+    template <>
+    struct type_lookup<PVType::Int32>
+    {
+        using type=std::int32_t;
+    };
+
+    template <>
+    struct type_lookup<PVType::UInt32>
+    {
+        using type=std::uint32_t;
+    };
+
+    template <>
+    struct type_lookup<PVType::Float64>
+    {
+        using type=double;
+    };
+
+    template <>
+    struct type_lookup<PVType::String>
+    {
+        using type=embedded::fixed_string<256>;
+    };
+}
+
+#endif // DAQD_TRUNK_EPICS_CHANNEL_TYPES_HH
diff --git a/src/epics/seq/fixed_size_string.hh b/src/epics/seq/fixed_size_string.hh
new file mode 100644
index 0000000000000000000000000000000000000000..822db31163758b66b3499761419a7942c76e3dde
--- /dev/null
+++ b/src/epics/seq/fixed_size_string.hh
@@ -0,0 +1,272 @@
+//
+// Created by jonathan.hanks on 8/16/18.
+//
+
+#ifndef FIXED_SIZE_STRING_FIXED_SIZE_STRING_HH
+#define FIXED_SIZE_STRING_FIXED_SIZE_STRING_HH
+
+#include <algorithm>
+#include <cstdarg>
+#include <cstddef>
+#include <cstdio>
+#include <cstring>
+
+namespace embedded
+{
+
+    /*!
+     * @brief A fixed size string abstraction that knows its bounds and does not
+     * overflow.
+     * @tparam max_size
+     * @note this class does not allocation and should not throw an exception
+     * ever.
+     */
+    template < std::size_t max_size >
+    class fixed_string
+    {
+        static_assert( max_size >= 1, "Fixed string capacity must be >= 1" );
+
+    public:
+        typedef std::size_t size_type;
+
+        /**
+         * @brief the buffer object is a way to get raw access to manipulate the
+         * fixed_string.  It looses most protections of the fixed_string class, but
+         * ensures that the buffer is null terminated when the buffer is destroyed.
+         */
+        class buffer
+        {
+            friend class fixed_string;
+            explicit buffer( char* buf ) : buf_{ buf }
+            {
+            }
+
+        public:
+            buffer( buffer&& other ) noexcept = default;
+            ~buffer( )
+            {
+                if ( buf_ )
+                {
+                    buf_[ fixed_string< max_size >::last_index( ) ] = '\0';
+                }
+            }
+            buffer( const buffer& other ) = delete;
+            buffer& operator=( const buffer& other ) = delete;
+            buffer& operator=( buffer&& other ) noexcept = default;
+
+            char*
+            data( ) noexcept
+            {
+                return buf_;
+            }
+            constexpr size_type
+                      capacity( ) const noexcept
+            {
+                return max_size;
+            }
+
+        private:
+            char* buf_;
+        };
+
+        fixed_string( ) noexcept : data_( )
+        {
+            data_[ 0 ] = '\0';
+        }
+        fixed_string( const fixed_string& ) noexcept = default;
+
+        template < std::size_t other_max_size >
+        explicit fixed_string(
+            const fixed_string< other_max_size >& other ) noexcept : data_( )
+        {
+            copy_in_data( other.c_str( ) );
+        }
+        explicit fixed_string( const char* in ) noexcept : data_( )
+        {
+            copy_in_data( in );
+        }
+
+        fixed_string& operator=( const fixed_string& other ) noexcept = default;
+
+        template < std::size_t other_max_size >
+        fixed_string&
+        operator=( const fixed_string< other_max_size >& other ) noexcept
+        {
+            operator=( other.c_str( ) );
+            return *this;
+        }
+
+        fixed_string&
+        operator=( const char* in ) noexcept
+        {
+            copy_in_data( in );
+            return *this;
+        }
+
+        fixed_string&
+        operator+=( const char* in ) noexcept
+        {
+            if ( in )
+            {
+                size_type cur_size = size( );
+                size_type in_size = std::min( std::strlen( in ), remaining( ) );
+                std::memcpy( data_ + cur_size, in, in_size );
+                data_[ cur_size + in_size ] = 0;
+            }
+            return *this;
+        }
+
+        template < std::size_t in_max >
+        fixed_string&
+        operator+=( const fixed_string< in_max >& in ) noexcept
+        {
+            size_type in_size = std::min( in.size( ), remaining( ) );
+            if ( in_size > 0 )
+            {
+                size_type cur_size = size( );
+                std::memcpy( data_ + cur_size, in.c_str( ), in_size );
+                data_[ cur_size + in_size ] = 0;
+            }
+            return *this;
+        }
+
+        explicit operator const char*( ) const noexcept
+        {
+            return c_str( );
+        }
+
+        void
+        clear( ) noexcept
+        {
+            data_[ 0 ] = '\0';
+        }
+
+        size_type
+        printf( const char* fmt, ... ) noexcept
+        {
+            if ( !fmt )
+            {
+                clear( );
+                return 0;
+            }
+            va_list args;
+            va_start( args, fmt );
+            size_type results = std::vsnprintf( data_, capacity( ), fmt, args );
+            va_end( args );
+            return results;
+        }
+
+        bool
+        operator!=( const fixed_string& other ) const noexcept
+        {
+            return !operator==( other );
+        }
+
+        bool
+        operator!=( const char* str ) const noexcept
+        {
+            return !operator==( str );
+        }
+
+        template < std::size_t other_max_size >
+        bool
+        operator!=( const fixed_string< other_max_size >& other ) const noexcept
+        {
+            return !operator==( other );
+        }
+
+        bool
+        operator==( const fixed_string& other ) const noexcept
+        {
+            return std::strcmp( c_str( ), other.c_str( ) ) == 0;
+        }
+
+        bool
+        operator==( const char* str ) const noexcept
+        {
+            static const char* dummy = "";
+            return std::strcmp( c_str( ), ( str ? str : dummy ) ) == 0;
+        }
+
+        template < std::size_t other_max_size >
+        bool
+        operator==( const fixed_string< other_max_size >& other ) const noexcept
+        {
+            return std::strcmp( c_str( ), other.c_str( ) ) == 0;
+        }
+
+        size_type
+        remaining( ) const noexcept
+        {
+            return last_index( ) - size( );
+        }
+
+        constexpr size_type
+                  capacity( ) const noexcept
+        {
+            return max_size;
+        }
+
+        size_type
+        size( ) const noexcept
+        {
+            return std::strlen( data_ );
+        }
+
+        const char*
+        c_str( ) const noexcept
+        {
+            return data_;
+        }
+
+        const char*
+        data( ) const noexcept
+        {
+            return data_;
+        }
+
+        void
+        pop_back_n( size_type n ) noexcept
+        {
+            if ( n > 0 )
+            {
+                auto length = size( );
+                auto remove = std::min( length, n );
+                data_[ length - remove ] = '\0';
+            }
+        }
+
+        buffer
+        get_buffer( ) noexcept
+        {
+            return buffer( data_ );
+        }
+
+    private:
+        void
+        copy_in_data( const char* in ) noexcept
+        {
+            if ( in )
+            {
+                size_type in_size =
+                    std::min( std::strlen( in ), last_index( ) );
+                std::memcpy( data_, in, in_size );
+                data_[ in_size ] = 0;
+            }
+            else
+            {
+                data_[ 0 ] = 0;
+            }
+        }
+
+        static constexpr size_type
+                         last_index( ) noexcept
+        {
+            return max_size - 1;
+        }
+        char data_[ max_size ];
+    };
+
+} // namespace embedded
+
+#endif // FIXED_SIZE_STRING_FIXED_SIZE_STRING_HH
diff --git a/src/epics/seq/fixed_size_vector.hh b/src/epics/seq/fixed_size_vector.hh
new file mode 100644
index 0000000000000000000000000000000000000000..6c060a5eff4ee7e80b63ccb7b81ba71cafab4fda
--- /dev/null
+++ b/src/epics/seq/fixed_size_vector.hh
@@ -0,0 +1,240 @@
+//
+// Created by jonathan.hanks on 11/13/20.
+//
+
+#ifndef DAQD_TRUNK_FIXED_SIZE_VECTOR_HH
+#define DAQD_TRUNK_FIXED_SIZE_VECTOR_HH
+
+#include <cstddef>
+#include <type_traits>
+#include <utility>
+
+namespace embedded
+{
+
+    /*!
+     * @brief a simple vector with a fixed backing store.
+     * @details
+     * @tparam T the type being stored, some constraints are place on T to keep
+     * the vector noexcept
+     * @tparam max_size
+     */
+    template < typename T, std::size_t max_size >
+    class fixed_size_vector
+    {
+        static_assert( std::is_nothrow_copy_constructible< T >::value,
+                       "T must be no throw copy constructable" );
+        static_assert( std::is_nothrow_copy_assignable< T >::value,
+                       "T must be no throw copy assignable" );
+        static_assert( std::is_nothrow_move_constructible< T >::value,
+                       "T must be no throw move constructable" );
+        static_assert( std::is_nothrow_move_assignable< T >::value,
+                       "T must be no throw move assignable" );
+        static_assert( std::is_nothrow_destructible< T >::value,
+                       "T must be no throw destructable" );
+
+        using storage_type =
+            typename std::aligned_storage< sizeof( T ), alignof( T ) >::type;
+
+    public:
+        using value_type = T;
+        using size_type = std::size_t;
+        using iterator = typename std::add_pointer< T >::type;
+        using const_iterator = typename std::add_pointer<
+            typename std::add_const< T >::type >::type;
+
+        ~fixed_size_vector( )
+        {
+            clear( );
+        }
+
+        size_type
+        size( ) const noexcept
+        {
+            return entries_;
+        }
+
+        bool
+        empty( ) const noexcept
+        {
+            return size( ) == 0;
+        }
+
+        constexpr size_type
+                  capacity( ) const noexcept
+        {
+            return max_size;
+        }
+
+        iterator
+        begin( ) noexcept
+        {
+            return reinterpret_cast< T* >( &storage_[ 0 ] );
+        }
+
+        iterator
+        end( ) noexcept
+        {
+            return begin( ) + entries_;
+        }
+
+        const_iterator
+        begin( ) const noexcept
+        {
+            return reinterpret_cast< const_iterator >( &storage_[ 0 ] );
+        }
+
+        const_iterator
+        end( ) const noexcept
+        {
+            return begin( ) + entries_;
+        }
+
+        const_iterator
+        cbegin( ) const noexcept
+        {
+            return begin( );
+        }
+
+        const_iterator
+        cend( ) const noexcept
+        {
+            return end( );
+        }
+
+        /*!
+         * @brief push an entry onto the back of the vector if space is
+         * available.
+         * @param val
+         * @return true on success, else false (no change made to the vector)
+         */
+        bool
+        push_back( const T& val ) noexcept
+        {
+            if ( size( ) < capacity( ) )
+            {
+                T* dest = reinterpret_cast< T* >( &storage_[ entries_ ] );
+                new ( dest ) T( std::move( val ) );
+                ++entries_;
+                return true;
+            }
+            return false;
+        }
+
+        /*!
+         * @brief push an entry onto the back of the vector if space is
+         * available.
+         * @param val
+         * @return true on success, else false (no change made to the vector)
+         */
+        bool
+        push_back( T&& val ) noexcept
+        {
+            if ( size( ) < capacity( ) )
+            {
+                T* dest = reinterpret_cast< T* >( &storage_[ entries_ ] );
+                new ( dest ) T( std::move( val ) );
+                ++entries_;
+                return true;
+            }
+            return false;
+        }
+
+        /*!
+         * @brief emplace an entry onto the back of the vector if space is
+         * available.
+         * @tparam Args The argument types to pass to T's constructor
+         * @param args The argument to pass to T's constructor
+         * @return true on success, else false (no change made to the vector)
+         */
+        template < typename... Args >
+        bool
+        emplace_back( Args... args ) noexcept
+        {
+            if ( size( ) < capacity( ) )
+            {
+                T* dest = reinterpret_cast< T* >( &storage_[ entries_ ] );
+                new ( dest ) T( std::forward< Args >( args )... );
+                ++entries_;
+                return true;
+            }
+            return false;
+        }
+
+        T& operator[]( size_type index ) noexcept
+        {
+            return *reinterpret_cast< T* >( &storage_[ index ] );
+        }
+
+        const T& operator[]( size_type index ) const noexcept
+        {
+            return *reinterpret_cast< const T* >( &storage_[ index ] );
+        }
+
+        void
+        pop_back( ) noexcept
+        {
+            if ( !empty( ) )
+            {
+                size_type index = entries_ - 1;
+                T*        cur = reinterpret_cast< T* >( &storage_[ index ] );
+                cur->~T( );
+                entries_--;
+            }
+        }
+
+        template < typename T_ = T,
+                   typename std::enable_if<
+                       std::is_trivially_destructible< T_ >::value,
+                       int >::type = 0 >
+        void
+        clear( )
+        {
+            entries_ = 0;
+        }
+
+        template < typename T_ = T,
+                   typename std::enable_if<
+                       !std::is_trivially_destructible< T_ >::value,
+                       int >::type = 0 >
+        void
+        clear( )
+        {
+            while ( !empty( ) )
+            {
+                pop_back( );
+            }
+        }
+
+        T&
+        front() noexcept
+        {
+            return operator[](0);
+        }
+
+        T&
+        back() noexcept
+        {
+            return operator[](entries_-1);
+        }
+
+        const T&
+        front() const noexcept
+        {
+            return operator[](0);
+        }
+
+        const T&
+        back() const noexcept
+        {
+            return operator[](entries_-1);
+        }
+
+    private:
+        storage_type storage_[ max_size ]{};
+        size_type    entries_{ 0 };
+    };
+
+} // namespace embedded
+
+#endif // DAQD_TRUNK_FIXED_SIZE_VECTOR_HH
diff --git a/src/epics/seq/main.c b/src/epics/seq/main.cc
similarity index 59%
rename from src/epics/seq/main.c
rename to src/epics/seq/main.cc
index 5631df33620b45eef0a9feab7ec742ae5be4f7cd..88cd9227a1c5cdd5fb0b4090684ef4179c771346 100644
--- a/src/epics/seq/main.c
+++ b/src/epics/seq/main.cc
@@ -19,6 +19,7 @@ of this distribution.
 /*
  * Main program for demo sequencer
  */
+#include "epics_channel.hh"
 #include <stddef.h>
 #include <stdlib.h>
 #include <string.h>
@@ -28,6 +29,10 @@ of this distribution.
 #include <sys/stat.h>
 #include <errno.h>
 #include <ctype.h>
+#include <algorithm>
+#include <array>
+#include <iterator>
+#include <memory>
 
 #ifdef USE_SYSTEM_TIME
 #include <time.h>
@@ -35,7 +40,9 @@ of this distribution.
 
 #include "iocsh.h"
 #include "dbStaticLib.h"
-#include "crc.h"
+extern "C" {
+    #include "crc.h"
+}
 #include "dbCommon.h"
 #include "recSup.h"
 #include "dbDefs.h"
@@ -45,28 +52,35 @@ of this distribution.
 #include "math.h"
 #include "rcgversion.h"
 #include "sdf_file_loaded.h"
+#include "util/user/check_file_crc.h"
 
 #define epicsExportSharedSymbols
 #include "asLib.h"
 #include "asCa.h"
 #include "asDbLib.h"
 
+#include "fixed_size_string.hh"
+#include "fixed_size_vector.hh"
+#include "simple_range.hh"
+
+using embedded::fixed_string;
+using embedded::fixed_size_vector;
+
 #ifdef CA_SDF
+#include <mutex>
 
-// The identifiers below were defined in the EPICS local db headers but
-// also are defined in cadef.h.
-// They are undefined here to avoid compiler warnings.
+// Including the DB/IOC based code + CA based client code gives warnings on redefinitions
+// so undef these before including the CA code.
 #undef DBR_SHORT
 #undef DBR_GR_LONG
 #undef DBR_GR_DOUBLE
-#undef DBR_CTRL_LONG
+#undef DBR_GR_LONG
 #undef DBR_CTRL_DOUBLE
+#undef DBR_CTRL_LONG
 #undef DBR_PUT_ACKT
 #undef DBR_PUT_ACKS
 #undef VALID_DB_REQ
 #undef INVALID_DB_REQ
-
-#include <pthread.h>
 #include "cadef.h"
 #endif
 
@@ -101,6 +115,14 @@ of this distribution.
 #define CA_STATE_RUNNING 2
 #define CA_STATE_EXIT 3
 
+enum SDF_TYPE {
+    SDF_NUM=0,
+    SDF_STR=1,
+    SDF_UNKNOWN=-1,
+};
+
+using PV_INT_TYPE = unsigned int;
+
 #if 0
 /// Pointers to status record
 DBENTRY  *pdbentry_status[2][10];
@@ -132,7 +154,7 @@ union Data {
 
 /// Structure for holding BURT settings in local memory.
 typedef struct CDS_CD_TABLE {
-	char chname[128];
+	fixed_string<128> chname;
 	int datatype;
 	union Data data;
 	int mask;
@@ -140,7 +162,7 @@ typedef struct CDS_CD_TABLE {
 	int filterswitch;
 	int filterNum;
 	int error;
-	char errMsg[64];
+	fixed_string<64> errMsg;
 	int chFlag;
 #ifdef CA_SDF
 	int connected;
@@ -149,7 +171,7 @@ typedef struct CDS_CD_TABLE {
 
 /// Structure for creating/holding filter module switch settings.
 typedef struct FILTER_TABLE {
-	char fname[64];
+	fixed_string<64> fname;
 	int swreq;
 	int swmask;
 	int sw[2];
@@ -160,15 +182,15 @@ typedef struct FILTER_TABLE {
 
 /// Structure for table data to be presented to operators.
 typedef struct SET_ERR_TABLE {
-	char chname[64];
-	char burtset[64];
-	char liveset[64];
-	char timeset[64];
-	char diff[64];
+	fixed_string<64> chname;
+	fixed_string<64> burtset;
+	fixed_string<64> liveset;
+	fixed_string<64> timeset;
+	fixed_string<64> diff;
 	double liveval;
 	int sigNum;
 	int chFlag;
-	int filtNum;
+	int filtNum{-1};
 	unsigned int sw[2];
 } SET_ERR_TABLE;
 
@@ -191,6 +213,15 @@ typedef struct CA_STARTUP_INFO {
 
 #endif
 
+using table_range = embedded::range<SET_ERR_TABLE*>;
+
+template <std::size_t entry_count>
+table_range
+make_range(fixed_size_vector<SET_ERR_TABLE, entry_count>& cont)
+{
+    return embedded::make_range(&cont[0], &cont[0] + cont.size());
+}
+
 // Gloabl variables		****************************************************************************************
 int chNum = 0;			///< Total number of channels held in the local lookup table.
 int chNotMon = 0;		///< Total number of channels not being monitored.
@@ -202,30 +233,30 @@ int chNotFound = 0;		///< Total number of channels read from BURT file which did
 int chNotInit = 0;		///< Total number of channels not initialized by the safe.snap BURT file.
 int rderror = 0;
 #ifndef USE_SYSTEM_TIME
-char timechannel[256];		///< Name of the GPS time channel for timestamping.
+std::unique_ptr< epics::DBEntry< epics::PVType::String > > timechannel{nullptr}; ///the GPS time channel for timestamping.
 #endif
 char reloadtimechannel[256];	///< Name of EPICS channel which contains the BURT reload requests.
 struct timespec t;
 char logfilename[128];
 
-CDS_CD_TABLE cdTable[SDF_MAX_TSIZE];		///< Table used to hold EPICS database info for monitoring settings.
-CDS_CD_TABLE cdTableP[SDF_MAX_CHANS];		///< Temp table filled on BURT read and set to EPICS channels.
-FILTER_TABLE filterTable[SDF_MAX_FMSIZE];			///< Table for holding filter module switch settings for comparisons.
-SET_ERR_TABLE setErrTable[SDF_ERR_TSIZE];	///< Table used to report settings diffs.
-SET_ERR_TABLE unknownChans[SDF_ERR_TSIZE];	///< Table used to report channels not found in local database.
-SET_ERR_TABLE uninitChans[SDF_ERR_TSIZE];	///< Table used to report channels not initialized by BURT safe.snap.
-SET_ERR_TABLE unMonChans[SDF_MAX_TSIZE];	///< Table used to report channels not being monitored.
-SET_ERR_TABLE readErrTable[SDF_ERR_TSIZE];	///< Table used to report file read errors..
-SET_ERR_TABLE cdTableList[SDF_MAX_TSIZE];	///< Table used to report file read errors..
-time_t timeTable[SDF_MAX_CHANS];		///< Holds timestamps for cdTableP
+fixed_size_vector<CDS_CD_TABLE, SDF_MAX_TSIZE> cdTable;       ///< Table used to hold EPICS database info for monitoring settings.
+fixed_size_vector<CDS_CD_TABLE, SDF_MAX_CHANS> cdTableP;      ///< Temp table filled on BURT read and set to EPICS channels.
+fixed_size_vector<FILTER_TABLE, SDF_MAX_FMSIZE> filterTable;  ///< Table for holding filter module switch settings for comparisons.
+fixed_size_vector<SET_ERR_TABLE, SDF_ERR_TSIZE> setErrTable;  ///< Table used to report settings diffs.
+fixed_size_vector<SET_ERR_TABLE, SDF_ERR_TSIZE> unknownChans; ///< Table used to report channels not found in local database.
+fixed_size_vector<SET_ERR_TABLE, SDF_ERR_TSIZE> uninitChans;  ///< Table used to report channels not initialized by BURT safe.snap.
+fixed_size_vector<SET_ERR_TABLE, SDF_MAX_TSIZE> unMonChans;	  ///< Table used to report channels not being monitored.
+fixed_size_vector<SET_ERR_TABLE, SDF_ERR_TSIZE> readErrTable; ///< Table used to report file read errors..
+fixed_size_vector<SET_ERR_TABLE, SDF_MAX_TSIZE> cdTableList;  ///< Table used to report file read errors..
+fixed_size_vector<time_t, SDF_MAX_CHANS> timeTable;           ///< Holds timestamps for cdTableP
 
-int filterMasks[SDF_MAX_FMSIZE];		///< Filter monitor masks, as manipulated by the user
+fixed_size_vector<int, SDF_MAX_FMSIZE> filterMasks;		///< Filter monitor masks, as manipulated by the user
 
-dbAddr fmMaskChan[SDF_MAX_FMSIZE];		///< We reflect the mask value into these channels
-dbAddr fmMaskChanCtrl[SDF_MAX_FMSIZE];		///< We signal bit changes to the masks on these channels
+fixed_size_vector<dbAddr, SDF_MAX_FMSIZE> fmMaskChan;		///< We reflect the mask value into these channels
+fixed_size_vector<dbAddr, SDF_MAX_FMSIZE> fmMaskChanCtrl;		///< We signal bit changes to the masks on these channels
 
 #ifdef CA_SDF
-SET_ERR_TABLE disconnectChans[SDF_MAX_TSIZE];
+fixed_size_vector<SET_ERR_TABLE, SDF_MAX_TSIZE> disconnectChans;
 
 // caTable, caConnTAble, chConnNum, chEnumDetermined are protected by the caTableMutex
 EPICS_CA_TABLE caTable[SDF_MAX_TSIZE];		    ///< Table used to hold the data returned by channel access
@@ -245,68 +276,65 @@ int chEnumDetermined = 0;			///< Total number of enums whos types have been dete
 long chDisconnected = 0;		///< Total number of channels that are disconnected.  This is used as the max index for disconnectedChans
 long chDisconnectedCount = 0;   ///< The count of the disconnected channels.  Seperate from chDisconnected so that it can be updated even when the table is not updated.
 
-pthread_t caThread;
-pthread_mutex_t caStateMutex;
-pthread_mutex_t caTableMutex;
-pthread_mutex_t caEvidMutex;
+
+std::mutex caStateMutex;
+std::mutex caTableMutex;
+std::mutex caEvidMutex;
+
+using lock_guard = std::lock_guard<std::mutex>;
 
 int caThreadState;
 long droppedPVCount;
 
-#define ADDRESS int
-#define SETUP setupCASDF();
-#define CLEANUP cleanupCASDF();
-#define GET_ADDRESS(NAME,ADDRP) getCAIndex((NAME),(ADDRP))
-#define PUT_VALUE(ADDR,TYPE,PVAL) setCAValue((ADDR),(TYPE),(PVAL))
-#define PUT_VALUE_INT(ADDR,PVAL) setCAValueEPICSLong((ADDR),(PVAL))
-#define GET_VALUE_NUM(ADDR,DESTP,TIMEP,CONNP) syncEpicsDoubleValue((ADDR),(DESTP),(TIMEP),(CONNP))
-#define GET_VALUE_INT(ADDR,DESTP,TIMEP,CONNP) syncEpicsIntValue((ADDR),(DESTP),(TIMEP),(CONNP))
-#define GET_VALUE_STR(ADDR,DESTP,LEN,TIMEP,CONNP) syncEpicsStrValue((ADDR),(DESTP),(LEN),(TIMEP),(CONNP))
+using ADDRESS = int;
+
+
 #else
-#define ADDRESS dbAddr
-#define SETUP
-#define CLEANUP
-#define GET_ADDRESS(NAME,ADDRP) dbNameToAddr((NAME),(ADDRP))
-#define PUT_VALUE(ADDR,TYPE,PVAL) dbPutField(&(ADDR),((TYPE)==SDF_NUM ? DBR_DOUBLE : DBR_STRING),(PVAL),1)
-#define PUT_VALUE_INT(ADDR,PVAL) dbPutField(&(ADDR),DBR_LONG,(int *)(PVAL),1);
-#define GET_VALUE_NUM(ADDR,DESTP,TIMEP,CONNP) getDbValueDouble(&(ADDR),(double*)(DESTP),(TIMEP))
-#define GET_VALUE_INT(ADDR,DESTP,TIMEP,CONNP) getDbValueLong(&(ADDR),(unsigned int*)(DESTP),(TIMEP))
-#define GET_VALUE_STR(ADDR,DESTP,LEN,TIMEP,CONNP) getDbValueString(&(ADDR),(char*)(DESTP),(LEN),(TIMEP))
+using ADDRESS = dbAddr;
 #endif
 
-#define SDF_NUM		0
-#define SDF_STR		1
-#define SDF_UNKNOWN	-1
-
 
 // Function prototypes		****************************************************************************************
-int isAlarmChannel(char *);
-int checkFileCrc(char *);
-int checkFileMod( char* , time_t* , int );
+bool isAlarmChannelRaw(const char *);
+int checkFileMod( const char* , time_t* , int );
 unsigned int filtCtrlBitConvert(unsigned int);
-void getSdfTime(char *, int size);
-void logFileEntry(char *);
-void getEpicsSettings(int, time_t *);
-int writeTable2File(char *,char *,int,CDS_CD_TABLE *);
-int savesdffile(int,int,char *,char *,char *,char *,char *,dbAddr,dbAddr,dbAddr); 
-int createSortTableEntries(int,int,char *,int *,time_t*);
-int reportSetErrors(char *,int,SET_ERR_TABLE *,int,int);
-int spChecker(int,SET_ERR_TABLE *,int,char *,int,int *);
-void newfilterstats(int);
-int writeEpicsDb(int,CDS_CD_TABLE *,int);
-int readConfig( char *,char *,int,char *);
+void getSdfTime(embedded::fixed_string<256>&);
+void logFileEntry(const char *);
+void getEpicsSettings();
+
+template <std::size_t entry_count>
+bool writeTable2File(const char *burtdir, const char *filename, int ftype, fixed_size_vector<CDS_CD_TABLE, entry_count>& myTable);
+
+int savesdffile(int,int,const char *,const char *,const char *,const char *,const char *,epics::DBEntry<epics::PVType::String>&,epics::DBEntry<epics::PVType::String>&,epics::DBEntry<epics::PVType::String>&);
+int createSortTableEntries(int,int,const char *,int *,time_t*);
+template <std::size_t entry_count>
+int reportSetErrors(char *,fixed_size_vector<SET_ERR_TABLE, entry_count>&,int,int);
+template <std::size_t entry_count>
+int spChecker(int,fixed_size_vector<SET_ERR_TABLE, entry_count>&,int,const char *,int,int *);
+void newfilterstats();
+
+template <std::size_t entry_count>
+int writeEpicsDb(fixed_size_vector<CDS_CD_TABLE, entry_count>&,int);
+int readConfig( const char *, const char *,int, const char *);
 int parseLine(char *, int,char *,char *,char *,char *,char *,char *);
-int modifyTable(int,SET_ERR_TABLE *);
-int resetSelectedValues(int,SET_ERR_TABLE *);
-void clearTableSelections(int,SET_ERR_TABLE *, int *);
-void setAllTableSelections(int,SET_ERR_TABLE *, int *,int);
-void changeSelectCB_uninit(int, SET_ERR_TABLE *, int *);
-void decodeChangeSelect(int, int, int, SET_ERR_TABLE *, int *, void (*)(int, SET_ERR_TABLE *, int *));
-int appendAlarms2File(char *,char *,char *);
+int modifyTable(table_range);
+template <std::size_t entry_count>
+int resetSelectedValues(fixed_size_vector<SET_ERR_TABLE, entry_count>&);
+template <std::size_t entry_count>
+void clearTableSelections(fixed_size_vector<SET_ERR_TABLE, entry_count>& dcsErrTable, int sc[]);
+void setAllTableSelections(table_range, int *,int);
+void changeSelectCB_uninit(SET_ERR_TABLE&, int *);
+void decodeChangeSelect(int, int, table_range, int *, void (*)(SET_ERR_TABLE&, int *));
+int appendAlarms2File(const char *,const char *,const char *);
 void registerFilters();
-void setupFMArrays(char *,int*,dbAddr*,dbAddr*);
-void resyncFMArrays(int *,dbAddr*);
-void processFMChanCommands(int*,dbAddr*,dbAddr*,int*,int,SET_ERR_TABLE*);
+void setupFMArrays(char *,fixed_size_vector<int, SDF_MAX_FMSIZE>&,
+        fixed_size_vector<dbAddr, SDF_MAX_FMSIZE>&,
+        fixed_size_vector<dbAddr, SDF_MAX_FMSIZE>&);
+void resyncFMArrays(fixed_size_vector<int, SDF_MAX_FMSIZE>&,fixed_size_vector<dbAddr, SDF_MAX_FMSIZE>&);
+void processFMChanCommands(fixed_size_vector<int, SDF_MAX_FMSIZE>&,
+        fixed_size_vector<dbAddr, SDF_MAX_FMSIZE>&,
+        fixed_size_vector<dbAddr, SDF_MAX_FMSIZE>&,
+        int*,table_range);
 void countDisconnectedChannels(int);
 void saveStrSetPoint(int, const char*);
 void updateStrVarSetPoint(int);
@@ -315,15 +343,15 @@ long countDisconnectedChans();
 
 void nullCACallback(struct event_handler_args);
 
-int getCAIndex(char *, ADDRESS *);
+bool getCAIndex(const char *, ADDRESS *);
 
 int canFindCAChannel(char *entry);
-int setCAValue(ADDRESS, int, void *);
-int setCAValueEPICSLong(ADDRESS, unsigned int *);
+bool setCAValue(ADDRESS, SDF_TYPE, const void *);
+bool setCAValueLong(ADDRESS, const PV_INT_TYPE *);
 
-int syncEpicsDoubleValue(ADDRESS, double *, time_t *, int *);
-int syncEpicsIntValue(ADDRESS, unsigned int *, time_t *, int *);
-int syncEpicsStrValue(ADDRESS, char *, int, time_t *, int *);
+bool syncEpicsDoubleValue(ADDRESS, double *, time_t *, int *);
+bool syncEpicsIntValue(ADDRESS, PV_INT_TYPE *, time_t *, int *);
+bool syncEpicsStrValue(ADDRESS, char *, int, time_t *, int *);
 
 void subscriptionHandler(struct event_handler_args);
 void connectCallback(struct connection_handler_args);
@@ -343,13 +371,100 @@ void *caMainLoop(void *);
 void initCAConnections(char *);
 void setupCASDF();
 void cleanupCASDF();
+
+void SETUP()
+{
+    setupCASDF();
+}
+void CLEANUP()
+{
+    cleanupCASDF();
+}
+
+bool GET_ADDRESS(const char* name, ADDRESS* addr)
+{
+    return getCAIndex(name, addr);
+}
+bool PUT_VALUE(ADDRESS addr, SDF_TYPE type, const void* pval)
+{
+    return setCAValue(addr, type, pval);
+}
+bool PUT_VALUE_INT(ADDRESS addr, const PV_INT_TYPE* val)
+{
+    return setCAValueLong(addr, val);
+}
+bool GET_VALUE_NUM(ADDRESS addr, double* dest, time_t* tp, int* connp)
+{
+    return syncEpicsDoubleValue(addr, dest, tp, connp);
+}
+bool GET_VALUE_INT(ADDRESS addr, PV_INT_TYPE* dest, time_t* tp, int* connp)
+{
+    return syncEpicsIntValue(addr, dest, tp, connp);
+}
+bool GET_VALUE_STR(ADDRESS addr, char* dest, int dest_size, time_t* tp, int *connp) {
+    return syncEpicsStrValue(addr, dest, dest_size, tp, connp);
+}
 #else
-int getDbValueDouble(ADDRESS*,double *,time_t *);
-int getDbValueString(ADDRESS*,char *, int, time_t *);
-int getDbValueLong(ADDRESS *,unsigned int * ,time_t *);
+bool getDbValueDouble(ADDRESS*,double *,time_t *);
+bool getDbValueString(ADDRESS*,char *, int, time_t *);
+bool getDbValueLong(ADDRESS *,PV_INT_TYPE * ,time_t *);
 void dbDumpRecords(DBBASE *,const char *);
+
+void SETUP()
+{}
+
+void CLEANUP()
+{}
+
+bool GET_ADDRESS(const char* name, ADDRESS* addr)
+{
+    return dbNameToAddr(name, addr) == 0;
+}
+
+bool PUT_VALUE(ADDRESS addr, SDF_TYPE type, const void* pval)
+{
+    return dbPutField(&addr,(type==SDF_NUM ? DBR_DOUBLE : DBR_STRING),pval,1) == 0;
+}
+bool PUT_VALUE_INT(ADDRESS addr, const PV_INT_TYPE *val)
+{
+    return dbPutField(&addr, DBR_LONG, val, 1) == 0;
+}
+bool GET_VALUE_NUM(ADDRESS addr, double* dest, time_t* tp, int* connp)
+{
+    return getDbValueDouble(&addr, dest, tp);
+}
+bool GET_VALUE_INT(ADDRESS addr, PV_INT_TYPE* dest, time_t* tp, int* connp)
+{
+    return getDbValueLong(&addr, dest, tp);
+}
+bool GET_VALUE_STR(ADDRESS addr, char* dest, int max_len, time_t *tp, int* connp)
+{
+    return getDbValueString(&addr, dest, max_len, tp);
+}
 #endif
 
+template <typename StringLike>
+bool
+PUT_VALUE_STR(ADDRESS addr, const StringLike& value)
+{
+    return PUT_VALUE(addr, SDF_STR, reinterpret_cast<void*>(const_cast<char*>(value.c_str())));
+}
+
+template <typename StringLike>
+bool
+GET_ADDRESS(const StringLike& name, ADDRESS* addr)
+{
+    return GET_ADDRESS(name.c_str(), addr);
+}
+
+template <typename StringLike>
+bool
+GET_VALUE_STR(ADDRESS addr, StringLike& dest, time_t *tp, int* connp)
+{
+    auto buffer = dest.get_buffer();
+    return GET_VALUE_STR(addr, buffer.data(), buffer.capacity(), tp, connp);
+}
+
 #ifdef VERBOSE_DEBUG
 #define D_NOW (__output_iter == 0)
 #define D(...) {if (__output_iter == 0) { fprintf(stderr, __VA_ARGS__); } }
@@ -385,16 +500,32 @@ void iterate_output_counter() {}
 //
 
 /// Given a channel name return 1 if it is an alarm channel, else 1
-int isAlarmChannel(char *chname) {
-	if (!chname) return 0;
-	return ((strstr(chname,".HIGH") != NULL) || 
-		(strstr(chname,".HIHI") != NULL) || 
-		(strstr(chname,".LOW") != NULL) || 
-		(strstr(chname,".LOLO") != NULL) || 
-		(strstr(chname,".HSV") != NULL) || 
-		(strstr(chname,".OSV") != NULL) || 
-		(strstr(chname,".ZSV") != NULL) || 
-		(strstr(chname,".LSV") != NULL) );
+template <typename StringLike>
+bool isAlarmChannel(const StringLike& chname) {
+	return isAlarmChannelRaw(chname.c_str());
+//    return ((strstr(chname.c_str(),".HIGH") != NULL) ||
+//		(strstr(chname.c_str(),".HIHI") != NULL) ||
+//		(strstr(chname.c_str(),".LOW") != NULL) ||
+//		(strstr(chname.c_str(),".LOLO") != NULL) ||
+//		(strstr(chname.c_str(),".HSV") != NULL) ||
+//		(strstr(chname.c_str(),".OSV") != NULL) ||
+//		(strstr(chname.c_str(),".ZSV") != NULL) ||
+//		(strstr(chname.c_str(),".LSV") != NULL) );
+}
+
+bool isAlarmChannelRaw(const char* chname) {
+    if (chname == nullptr)
+    {
+        return false;
+    }
+    return ((strstr(chname,".HIGH") != NULL) ||
+            (strstr(chname,".HIHI") != NULL) ||
+            (strstr(chname,".LOW") != NULL) ||
+            (strstr(chname,".LOLO") != NULL) ||
+            (strstr(chname,".HSV") != NULL) ||
+            (strstr(chname,".OSV") != NULL) ||
+            (strstr(chname,".ZSV") != NULL) ||
+            (strstr(chname,".LSV") != NULL) );
 }
 
 /// Common routine to parse lines read from BURT/SDF files..
@@ -481,12 +612,16 @@ int parseLine(char *s, int bufSize, char *str1, char *str2, char *str3, char *st
 ///	@param[in] numEntries		Number of entries in the table.	
 ///	@param[in] dcsErrtable 		Pointer to table
 ///	@param[out] sc 			Pointer to change counters.
-void clearTableSelections(int numEntries,SET_ERR_TABLE *dcsErrTable, int sc[])
+template <std::size_t entry_count>
+void clearTableSelections(fixed_size_vector<SET_ERR_TABLE, entry_count>& dcsErrTable, int sc[])
 {
-int ii;
+    int ii;
 	for(ii=0;ii<3;ii++) sc[ii] = 0;
-	for(ii=0;ii<numEntries;ii++) dcsErrTable[ii].chFlag = 0;
-	for(ii=0;ii<numEntries;ii++) dcsErrTable[ii].filtNum = -1;
+	for (auto& entry:dcsErrTable)
+    {
+	    entry.chFlag = 0;
+	    entry.filtNum = -1;
+    }
 }
 
 
@@ -495,40 +630,43 @@ int ii;
 ///	@param[in] dcsErrtable 		Pointer to table
 ///	@param[out] sc 			Pointer to change counters.
 ///	@param[in] selectOp 		Present selection.
-void setAllTableSelections(int numEntries,SET_ERR_TABLE *dcsErrTable, int sc[],int selectOpt)
+void setAllTableSelections(table_range dcsErrTable, int sc[],int selectOpt)
 {
-int ii;
-int filtNum = -1;
-long status = 0;
+    int filtNum = -1;
+    long status = 0;
+
 	switch(selectOpt) {
 		case 1:
 			sc[0] = 0;
-			for(ii=0;ii<numEntries;ii++) {
-				dcsErrTable[ii].chFlag |= 2;
+			for (auto& entry:dcsErrTable)
+			{
+				entry.chFlag |= 2;
 				sc[0] ++;
-				if(dcsErrTable[ii].chFlag & 4) {
-					dcsErrTable[ii].chFlag &= ~(4);
+				if(entry.chFlag & 4) {
+					entry.chFlag &= ~(4);
 					sc[1] --;
 				}
 			}
 			break;
 		case 2:
 			sc[1] = 0;
-			for(ii=0;ii<numEntries;ii++) {
-				dcsErrTable[ii].chFlag |= 4;
+            for (auto& entry:dcsErrTable)
+			{
+				entry.chFlag |= 4;
 				sc[1] ++;
-				if(dcsErrTable[ii].chFlag & 2) {
-					dcsErrTable[ii].chFlag &= ~(2);
+				if(entry.chFlag & 2) {
+					entry.chFlag &= ~(2);
 					sc[0] --;
 				}
 			}
 			break;
 		case 3:
-			sc[2] = numEntries;
-			for(ii=0;ii<numEntries;ii++) {
-				dcsErrTable[ii].chFlag |= 8;
+			sc[2] = std::distance(dcsErrTable.begin(), dcsErrTable.end());
+            for (auto& entry:dcsErrTable)
+			{
+				entry.chFlag |= 8;
 				
-				filtNum = dcsErrTable[ii].filtNum;
+				filtNum = entry.filtNum;
 				if (filtNum >= 0) {
 					if (filterMasks[filtNum] == filterTable[filtNum].mask) {
 						filterMasks[filtNum] = ((ALL_SWSTAT_BITS & filterMasks[filtNum]) > 0 ? 0 : ~0);
@@ -546,12 +684,12 @@ long status = 0;
 ///	@param[in] index		Number providing the index into the dcsErrTable
 ///	@param[in] dcsErrTable		Pointer to table
 ///	@param[out] selectCounter	Pointer to change counters
-void changeSelectCB_uninit(int index, SET_ERR_TABLE *dcsErrTable, int selectCounter[])
+void changeSelectCB_uninit(SET_ERR_TABLE &errTableEntry, int selectCounter[])
 {
-	int revertBit = dcsErrTable[index].chFlag & 2;
-	int acceptBit = dcsErrTable[index].chFlag & 4;
-	int monBit = dcsErrTable[index].chFlag & 8;
-	int otherBits = 0; //dcsErrTable[index].chFlag | ~(2 | 4 | 8);
+	int revertBit = errTableEntry.chFlag & 2;
+	int acceptBit = errTableEntry.chFlag & 4;
+	int monBit = errTableEntry.chFlag & 8;
+	int otherBits = 0; //errTableEntry.chFlag | ~(2 | 4 | 8);
 
 	// revert is not defined on this table
 	if (revertBit) {
@@ -563,7 +701,7 @@ void changeSelectCB_uninit(int index, SET_ERR_TABLE *dcsErrTable, int selectCoun
 		acceptBit = 4;
 		selectCounter[1] ++;
 	} 
-	dcsErrTable[index].chFlag = revertBit | acceptBit | monBit | otherBits;
+	errTableEntry.chFlag = revertBit | acceptBit | monBit | otherBits;
 }
 
 /// Common routine to set/unset individual entries for table changes..
@@ -572,40 +710,42 @@ void changeSelectCB_uninit(int index, SET_ERR_TABLE *dcsErrTable, int selectCoun
 ///	@param[in] totalItems		Number items selected for change.
 ///	@param[in] dcsErrtable 		Pointer to table
 ///	@param[out] selectCounter 	Pointer to change counters.
-void decodeChangeSelect(int selNum, int page, int totalItems, SET_ERR_TABLE *dcsErrTable, int selectCounter[], void (*cb)(int, SET_ERR_TABLE*, int*))
+void decodeChangeSelect(int selNum, int page, table_range dcsErrTable, int selectCounter[], void (*cb)(SET_ERR_TABLE&, int*))
 {
-int selectBit;
-int selectLine;
+    int selectBit;
+    int selectLine;
 
 	selectBit = selNum / 100;
 	selectLine = selNum % 100 - 1;
 	selNum += page * SDF_ERR_DSIZE;
 	selectLine += page * SDF_ERR_DSIZE;
-	if(selectLine < totalItems) {
+
+	auto selection = dcsErrTable.begin() + selectLine;
+	if(selection < dcsErrTable.end()) {
 		switch (selectBit) {
 			case 0:
-				dcsErrTable[selectLine].chFlag ^= 2;
-				if(dcsErrTable[selectLine].chFlag & 2) selectCounter[0] ++;
+				selection->chFlag ^= 2;
+				if(selection->chFlag & 2) selectCounter[0] ++;
 				else selectCounter[0] --;
-				if(dcsErrTable[selectLine].chFlag & 4) {
-					dcsErrTable[selectLine].chFlag &= ~(4);
+				if(selection->chFlag & 4) {
+					selection->chFlag &= ~(4);
 					selectCounter[1] --;
 				}
 				break;
 			case 1:
-				dcsErrTable[selectLine].chFlag ^= 4;
-				if(dcsErrTable[selectLine].chFlag & 4) selectCounter[1] ++;
+				selection->chFlag ^= 4;
+				if(selection->chFlag & 4) selectCounter[1] ++;
 				else selectCounter[1] --;
-				if(dcsErrTable[selectLine].chFlag & 2) {
-					dcsErrTable[selectLine].chFlag &= ~(2);
+				if(selection->chFlag & 2) {
+					selection->chFlag &= ~(2);
 					selectCounter[0] --;
 				}
 				break;
 			case 2:
 				/* we only handle non-filter bank entries here */
-				if (dcsErrTable[selectLine].filtNum < 0) {
-					dcsErrTable[selectLine].chFlag ^= 8;
-					if(dcsErrTable[selectLine].chFlag & 8) selectCounter[2] ++;
+				if (selection->filtNum < 0) {
+					selection->chFlag ^= 8;
+					if(selection->chFlag & 8) selectCounter[2] ++;
 					else selectCounter[2] --;
 				}
 				break;
@@ -613,13 +753,13 @@ int selectLine;
 				break;
 		}
 		if (cb)
-			cb(selectLine, dcsErrTable, selectCounter);
+			cb(*selection, selectCounter);
 	}
 }
 
 // Check for file modification time changes
 int
-checkFileMod( char* fName, time_t* last_mod_time, int gettime )
+checkFileMod( const char* fName, time_t* last_mod_time, int gettime )
 {
     struct stat statBuf;
     if ( !stat( fName, &statBuf ) )
@@ -645,32 +785,7 @@ checkFileMod( char* fName, time_t* last_mod_time, int gettime )
     }
 }
 
-/// Common routine to check file CRC.
-///	@param[in] *fName	Name of file to check.
-///	@return File CRC or -1 if file not found.
-int checkFileCrc(char *fName)
-{
-    char         cbuf[ 128 ];
-    char*        cp;
-    int          flen = 0;
-    int          clen = 0;
-    unsigned int crc = 0;
-    FILE*        fp = fopen( fName, "r" );
-    if ( fp == NULL )
-    {
-        return 0;
-    }
 
-    while ( ( cp = fgets( cbuf, 128, fp ) ) != NULL )
-    {
-        clen = strlen( cbuf );
-        flen += clen;
-        crc = crc_ptr( cbuf, clen, crc );
-    }
-    crc = crc_len( flen, crc );
-    fclose( fp );
-    return crc;
-}
 
 /// Quick convert of filter switch settings to match SWMASK
 ///	@param[in] v	UINT32 representation of filter module switch setting.
@@ -687,7 +802,8 @@ unsigned int filtCtrlBitConvert(unsigned int v) {
 ///	@param[in] v			SWSTAT representation of switch setting.	
 ///	@param[out] filterstring	Returned string representation of switch setting.	
 ///	@return				Always zero at this point.
-int filtStrBitConvert(int stype, unsigned int v, char *filterstring) {
+template <typename StringLike>
+int filtStrBitConvert(int stype, unsigned int v, StringLike& filterstring) {
 unsigned int val[5],jj;
 unsigned int x;
 char bitDefShort[16][8] = {"IN,","OF,","1,","2,",
@@ -705,26 +821,26 @@ char bitDefLong[16][8] = {"IN,","OF,","F1,","F2,",
 	val[3] = (v & 0x1000) << 1;		// Output
 	val[4] = (v & 0x18000) >> 1;		// Dec and Hold
 	x = val[0] + val[1] + val[2] + val[3] + val[4];
-	sprintf(filterstring,"%s","");
+	filterstring.clear();
 	for(jj=0;jj<16;jj++) {
-		if(x & 1 && stype == 0) strcat(filterstring,bitDefShort[jj]);
-		if(x & 1 && stype == 1) strcat(filterstring,bitDefLong[jj]);
+		if(x & 1 && stype == 0) filterstring += bitDefShort[jj];
+		if(x & 1 && stype == 1) filterstring += bitDefLong[jj];
 		x = x >> 1;
 	}
-	filterstring[strlen(filterstring) - 1] = 0;
 	// strcat(filterstring,"\0");
         return(0);
 }
 
 /// Given the a filter table entry print out the bits set in the mask.
 /// @param[in] filter the filter table entry to extract the current value from
-/// @param[out] dest buffer to put the results in. must be >= 64 bytes
-void createSwitchText(FILTER_TABLE *filter, char *dest) {
+/// @param[out] dest buffer to put the results in
+template <typename StringLike>
+void createSwitchText(FILTER_TABLE *filter, StringLike& dest) {
 #if 1
 	int sw1s=0;
 	int sw2s=0;
 
-	dest[0] = 0;
+	dest.clear();
 	sw1s = (int)cdTableP[filter->sw[0]].data.chval;
 	sw2s = (int)cdTableP[filter->sw[1]].data.chval;
 #else
@@ -750,10 +866,10 @@ void createSwitchText(FILTER_TABLE *filter, char *dest) {
 }
 
 /// Routine to check filter switch settings and provide info on switches not set as requested.
-/// @param[in] fcount	Number of filters in the control model.
 /// @param[in] monitorAll	Global monitoring flag.
 /// @return	Number of errant switch settings found.
-int checkFilterSwitches(int fcount, SET_ERR_TABLE setErrTable[], int monitorAll, int displayall, int wcflag, char *wcstr, int *diffcntr)
+template <std::size_t entry_count>
+int checkFilterSwitches(fixed_size_vector<SET_ERR_TABLE, entry_count>& setErrTable, int monitorAll, int displayall, int wcflag, const char *wcstr, int *diffcntr)
 {
 unsigned int refVal=0;
 unsigned int presentVal=0;
@@ -761,15 +877,14 @@ unsigned int x=0,y=0;
 unsigned int mask = 0;
 unsigned int maskedRefVal = 0;
 unsigned int maskedSwReqVal = 0;
-int ii=0,jj=0;
+int jj=0;
 int errCnt = 0;
-char swname[3][64];
-char tmpname[64];
+fixed_string<64>swname[3];
+fixed_string<64> tmpname;
 time_t mtime=0;
-char localtimestring[256];
-char swstrE[64];
-char swstrB[64];
-char swstrD[64];
+fixed_string<64> swstrE;
+fixed_string<64> swstrB;
+fixed_string<64> swstrD;
 struct buffer {
 	time_t t;
 	unsigned int rval;
@@ -779,38 +894,36 @@ ADDRESS paddr1;
 ADDRESS paddr2;
 long status=0;
 
-    swstrE[0]='\0';
-    swstrB[0]='\0';
-    swstrD[0]='\0';
-	for(ii=0;ii<fcount;ii++)
-	{
+    auto filtNum = -1;
+    for (const auto& curFilt:filterTable)
+    {
+        ++filtNum;
 		bzero(buffer, sizeof(struct buffer)*2);
-		bzero(swname[0],sizeof(swname[0]));
-		bzero(swname[1],sizeof(swname[1]));
-		strcpy(swname[0],filterTable[ii].fname);
-		strcat(swname[0],"SW1S");
-		strcpy(swname[1],filterTable[ii].fname);
-		strcat(swname[1],"SW2S");
-		sprintf(swname[2],"%s",filterTable[ii].fname);
-		strcat(swname[2],"SWSTR");
-		status = GET_ADDRESS(swname[0],&paddr);
-		status = GET_VALUE_INT(paddr,&(buffer[0].rval),&(buffer[0].t), 0);
-		status = GET_ADDRESS(swname[1],&paddr1);
-		status = GET_VALUE_INT(paddr1,&(buffer[1].rval),&(buffer[1].t), 0);
+
+		swname[0] = curFilt.fname;
+		swname[0] += "SW1S";
+		swname[1] = curFilt.fname;
+		swname[1] += "SW2S";
+		swname[2] = curFilt.fname;
+		swname[2] += "SWSTR";
+		GET_ADDRESS(swname[0],&paddr);
+		GET_VALUE_INT(paddr,&(buffer[0].rval),&(buffer[0].t), 0);
+		GET_ADDRESS(swname[1],&paddr1);
+		GET_VALUE_INT(paddr1,&(buffer[1].rval),&(buffer[1].t), 0);
 		for(jj=0;jj<2;jj++) {
 			if(buffer[jj].rval > 0xffff || buffer[jj].rval < 0)	// Switch setting overrange
 			{
-				sprintf(setErrTable[errCnt].chname,"%s", swname[jj]);
-				sprintf(setErrTable[errCnt].burtset, "%s", " ");
-				sprintf(setErrTable[errCnt].liveset, "0x%x", buffer[jj].rval);
-				sprintf(setErrTable[errCnt].diff, "%s", "OVERRANGE");
-				setErrTable[errCnt].liveval = 0.0;
-				setErrTable[errCnt].sigNum = filterTable[ii].sw[jj];
+			    setErrTable.emplace_back();
+			    auto& curErr = setErrTable.back();
+				curErr.chname = swname[jj];
+				curErr.burtset = " ";
+				curErr.liveset.printf("0x%x", buffer[jj].rval);
+				curErr.diff = "OVERRANGE";
+				curErr.liveval = 0.0;
+				curErr.sigNum = curFilt.sw[jj];
 				// take the latest change time between the SW1S & SW2S channels
 				mtime = (buffer[0].t >= buffer[1].t ? buffer[0].t : buffer[1].t);
-				strcpy(localtimestring, ctime(&mtime));
-				localtimestring[strlen(localtimestring) - 1] = 0;
-				sprintf(setErrTable[errCnt].timeset, "%s", localtimestring);
+				curErr.timeset = ctime(&mtime);
 				buffer[jj].rval &= 0xffff;
 				errCnt ++;
 			}
@@ -819,44 +932,43 @@ long status=0;
 		refVal = filtCtrlBitConvert(presentVal);
 		filtStrBitConvert(0,refVal,swstrE);
 		x = refVal;
-		status = GET_ADDRESS(swname[2],&paddr2);
-		status = PUT_VALUE(paddr2,SDF_STR,swstrE);
-		setErrTable[errCnt].filtNum = -1;
+		GET_ADDRESS(swname[2],&paddr2);
+		PUT_VALUE_STR(paddr2,swstrE);
+		//setErrTable[errCnt].filtNum = -1;
 
-		mask = (unsigned int)filterTable[ii].mask;
+		mask = (unsigned int)curFilt.mask;
 		maskedRefVal = refVal & mask;
-		maskedSwReqVal = filterTable[ii].swreq & mask;
-		if(( maskedRefVal != maskedSwReqVal && errCnt < SDF_ERR_TSIZE && (mask || monitorAll) && filterTable[ii].init) )
+		maskedSwReqVal = curFilt.swreq & mask;
+		if(( maskedRefVal != maskedSwReqVal && errCnt < SDF_ERR_TSIZE && (mask || monitorAll) && curFilt.init) )
 			*diffcntr += 1;
-		if(( maskedRefVal != maskedSwReqVal && errCnt < SDF_ERR_TSIZE && (mask || monitorAll) && filterTable[ii].init) || displayall)
+		if(( maskedRefVal != maskedSwReqVal && errCnt < SDF_ERR_TSIZE && (mask || monitorAll) && curFilt.init) || displayall)
 		{
 			filtStrBitConvert(1,refVal,swstrE);
-			filtStrBitConvert(1,filterTable[ii].swreq,swstrB);
+			filtStrBitConvert(1,curFilt.swreq,swstrB);
 			x = maskedRefVal ^ maskedSwReqVal;
 			filtStrBitConvert(1,x,swstrD);
-			bzero(tmpname,sizeof(tmpname));
-			strncpy(tmpname,filterTable[ii].fname,(strlen(filterTable[ii].fname)-1));
-
-			if(!wcflag || (wcflag && (strstr(tmpname,wcstr) != NULL))) {
-			sprintf(setErrTable[errCnt].chname,"%s", tmpname);
-			sprintf(setErrTable[errCnt].burtset, "%s", swstrB);
-			sprintf(setErrTable[errCnt].liveset, "%s", swstrE);
-			sprintf(setErrTable[errCnt].diff, "%s", swstrD);
-			
-			setErrTable[errCnt].liveval = 0.0;
-			setErrTable[errCnt].sigNum = filterTable[ii].sw[0] + (filterTable[ii].sw[1] * SDF_MAX_TSIZE);
-			setErrTable[errCnt].filtNum = ii;
-			setErrTable[errCnt].sw[0] = buffer[0].rval;
-			setErrTable[errCnt].sw[1] = buffer[1].rval;
-			if(filterTable[ii].mask & ALL_SWSTAT_BITS) setErrTable[errCnt].chFlag |= 0x1;
-			else setErrTable[errCnt].chFlag &= 0xe;
-
-			// take the latest change time between the SW1S & SW2S channels
-			mtime = (buffer[0].t >= buffer[1].t ? buffer[0].t : buffer[1].t);
-			strcpy(localtimestring, ctime(&mtime));
-			localtimestring[strlen(localtimestring) - 1] = 0;
-			sprintf(setErrTable[errCnt].timeset, "%s", localtimestring);
-			errCnt ++;
+			tmpname = curFilt.fname;
+
+			if(!wcflag || (wcflag && (strstr(tmpname.c_str(),wcstr) != NULL))) {
+			    setErrTable.emplace_back();
+			    auto& curErr = setErrTable.back();
+                curErr.chname = tmpname;
+                curErr.burtset = swstrB;
+                curErr.liveset = swstrE;
+                curErr.diff = swstrD;
+
+                curErr.liveval = 0.0;
+                curErr.sigNum = curFilt.sw[0] + (curFilt.sw[1] * SDF_MAX_TSIZE);
+                curErr.filtNum = filtNum;
+                curErr.sw[0] = buffer[0].rval;
+                curErr.sw[1] = buffer[1].rval;
+                if(curFilt.mask & ALL_SWSTAT_BITS) curErr.chFlag |= 0x1;
+                else curErr.chFlag &= 0xe;
+
+                // take the latest change time between the SW1S & SW2S channels
+                mtime = (buffer[0].t >= buffer[1].t ? buffer[0].t : buffer[1].t);
+                curErr.timeset = ctime(&mtime);
+                errCnt ++;
 			}
 		}
 	}
@@ -866,7 +978,7 @@ long status=0;
 /// Routine for reading and formatting the time as a string.
 ///	@param[out] timestring 	Pointer to char string in which GPS time is to be written.
 /// @note This can use the GPS time from the model, or if configured with USE_SYSTEM_TIME the system time.
-void getSdfTime(char *timestring, int size)
+void getSdfTime(embedded::fixed_string<256>& timestring)
 {
 #ifdef USE_SYSTEM_TIME
 	time_t t=0;
@@ -874,32 +986,29 @@ void getSdfTime(char *timestring, int size)
 
 	t = time(NULL);
 	localtime_r(&t, &tdata);
-	if (timestring && size > 0) {
-		if (strftime(timestring, size, "%a %b %e %H:%M:%S %Y", &tdata) == 0) {
-			timestring[0] = '\0';
-		}
-	}
+
+    auto buf = timestring.get_buffer();
+    strftime(buf.data(), buf.capacity(), "%a %b %e %H:%M:%S %Y", &tdata);
 #else
 	dbAddr paddr;
 	long ropts = 0;
 	long nvals = 1;
 	long status;
 
-	status = dbNameToAddr(timechannel,&paddr);
-	status = dbGetField(&paddr,DBR_STRING,timestring,&ropts,&nvals,NULL);
+    timechannel->get(timestring);
 #endif
 }
 
 /// Routine for logging messages to ioc.log file.
 /// 	@param[in] message Ptr to string containing message to be logged.
-void logFileEntry(char *message)
+void logFileEntry(const char *message)
 {
 	FILE *log=0;
-	char timestring[256];
+	embedded::fixed_string<256> timestring{};
 	long status=0;
 	dbAddr paddr;
 
-	getSdfTime(timestring, 256);
+	getSdfTime(timestring);
 	log = fopen(logfilename,"a");
 	if(log == NULL) {
 		status = dbNameToAddr(reloadtimechannel,&paddr);
@@ -916,43 +1025,36 @@ void logFileEntry(char *message)
 /// Function to read all settings, listed in main table, from the EPICS database.
 ///	@param[in] numchans The number of channels listed in the main table.
 ///	@param[out] times (Optional) An array of at least numchans time_t values to put change times in
-void getEpicsSettings(int numchans, time_t *times)
+void getEpicsSettings()
 {
-int ii;
-long status;
-int chcount = 0;
-double dval = 0;
-ADDRESS geaddr;
-long statusR = 0;
-char sval[64];
-time_t *change_t = 0;
-
-	for(ii=0;ii<numchans;ii++)
-	{
-		// Load main table settings into temp (cdTableP)
-		sprintf(cdTableP[ii].chname,"%s",cdTable[ii].chname);
-		cdTableP[ii].datatype = cdTable[ii].datatype;
-		cdTableP[ii].mask = cdTable[ii].mask;
-		cdTableP[ii].initialized = cdTable[ii].initialized;
-		// Find address of channel
-		status = GET_ADDRESS(cdTableP[ii].chname,&geaddr);
-		if(!status) {
-			change_t = ( times ? &times[ii] : 0 );
-			if(cdTableP[ii].datatype == SDF_NUM)
-			{
-				statusR = GET_VALUE_NUM(geaddr,&dval,change_t, NULL); //&(cdTableP[ii].connected));
-				if (!statusR) cdTableP[ii].data.chval = (cdTable[ii].filterswitch ? (int)dval & 0xffff : dval);
-			} else {
-				statusR = GET_VALUE_STR(geaddr,sval,sizeof(sval),change_t, NULL); //&(cdTableP[ii].connected));
-				if(!statusR) sprintf(cdTableP[ii].data.strval,"%s",sval);
-			}
-			chcount ++;
-		}
-	}
-}
-
-void encodeBURTString(char *src, char *dest, int dest_size) {
-	char *ch = 0;
+    cdTableP.clear();
+    std::transform(cdTable.begin(), cdTable.end(), std::back_inserter( cdTableP),
+                   [](const CDS_CD_TABLE& entry) -> CDS_CD_TABLE {
+        CDS_CD_TABLE result;
+        result.chname = entry.chname;
+        result.datatype = entry.datatype;
+        result.mask = entry.mask;
+        result.initialized = entry.initialized;
+
+        ADDRESS geaddr;
+        if(GET_ADDRESS(result.chname,&geaddr)) {
+            time_t* change_t = nullptr;
+           if(result.datatype == SDF_NUM)
+           {
+               double dval;
+               if (GET_VALUE_NUM(geaddr,&dval,change_t, NULL)) {
+                   result.data.chval = (entry.filterswitch ? (int) dval & 0xffff : dval);
+               }
+           } else {
+               GET_VALUE_STR(geaddr,result.data.strval, sizeof(result.data.strval),change_t, NULL);
+           }
+       }
+        return result;
+    });
+}
+
+void encodeBURTString(const char *src, char *dest, int dest_size) {
+	const char *ch = 0;
 	int expand = 0;
 
 	if (!src || !dest || dest_size < 1) return;
@@ -982,15 +1084,16 @@ void encodeBURTString(char *src, char *dest, int dest_size) {
 #define SDF_FILE_PARAMS_ONLY	1
 #define SDF_FILE_BURT_ONLY	2
 /// Common routine for saving table data to BURT files.
-int writeTable2File(char *burtdir,
-		    char *filename, 		///< Name of file to write
+template <std::size_t entry_count>
+bool writeTable2File(const char *burtdir,
+                     const char *filename, 		///< Name of file to write
 		    int ftype, 			///< Type of file to write
-		    CDS_CD_TABLE myTable[])	///< Table to be written.
+		    fixed_size_vector<CDS_CD_TABLE, entry_count>& myTable)	///< Table to be written.
 {
         int ii;
         FILE *csFile=0;
         char filemsg[128];
-	char timestring[128];
+	embedded::fixed_string<256> timestring;
 	char monitorstring[128];
 	char burtString[64+2];
 #ifdef CA_SDF
@@ -1006,12 +1109,12 @@ int writeTable2File(char *burtdir,
         {
             sprintf(filemsg,"ERROR Failed to open %s - %s",filename,strerror(errno));
             logFileEntry(filemsg);
-	    return(-1);
+	    return(false);
         }
 	// Write BURT header
-	getSdfTime(timestring, 128);
+	getSdfTime(timestring);
 	fprintf(csFile,"%s\n","--- Start BURT header");
-	fprintf(csFile,"%s%s\n","Time:      ",timestring);
+	fprintf(csFile,"%s%s\n","Time:      ",timestring.c_str());
 	fprintf(csFile,"%s\n","Login ID: controls ()");
 	fprintf(csFile,"%s\n","Eff  UID: 1001 ");
 	fprintf(csFile,"%s\n","Group ID: 1001 ");
@@ -1022,18 +1125,18 @@ int writeTable2File(char *burtdir,
 	fprintf(csFile,"%s\n","Req File: autoBurt.req ");
 	fprintf(csFile,"%s\n","--- End BURT header");
 
-	for(ii=0;ii<chNum;ii++)
+	for (const auto& entry:myTable)
 	{
-		if (myTable[ii].filterswitch) {
-			if ((myTable[ii].mask & ALL_SWSTAT_BITS)== ~0) {
+		if (entry.filterswitch) {
+			if ((entry.mask & ALL_SWSTAT_BITS)== ~0) {
 				strcpy(monitorstring, "1");
-			} else if ((myTable[ii].mask & ALL_SWSTAT_BITS) == 0) {
+			} else if ((entry.mask & ALL_SWSTAT_BITS) == 0) {
 				strcpy(monitorstring, "0");
 			} else {
-				snprintf(monitorstring, sizeof(monitorstring), "0x%x", myTable[ii].mask);
+				snprintf(monitorstring, sizeof(monitorstring), "0x%x", entry.mask);
 			}
 		} else {
-			if (myTable[ii].mask)
+			if (entry.mask)
 				strcpy(monitorstring,"1");
 			else
 				strcpy(monitorstring,"0");
@@ -1042,28 +1145,28 @@ int writeTable2File(char *burtdir,
 		{
 		   case SDF_WITH_INIT_FLAG:
 			if(myTable[ii].datatype == SDF_NUM) {
-				fprintf(csFile,"%s %d %.*e %s %d\n",myTable[ii].chname,1,precision,myTable[ii].data.chval,monitorstring,myTable[ii].initialized);
+				fprintf(csFile,"%s %d %.*e %s %d\n",entry.chname,1,precision,entry.data.chval,monitorstring,entry.initialized);
 			} else {
-				encodeBURTString(myTable[ii].data.strval, burtString, sizeof(burtString));
-				fprintf(csFile,"%s %d %s %s %d\n",myTable[ii].chname,1,burtString,monitorstring,myTable[ii].initialized);
+				encodeBURTString(entry.data.strval, burtString, sizeof(burtString));
+				fprintf(csFile,"%s %d %s %s %d\n",entry.chname,1,burtString,monitorstring,entry.initialized);
 			}
 			break;
 		   case SDF_FILE_PARAMS_ONLY:
-			if(myTable[ii].initialized) {
-				if(myTable[ii].datatype == SDF_NUM) {
-					fprintf(csFile,"%s %d %.*e %s\n",myTable[ii].chname,1,precision,myTable[ii].data.chval,monitorstring);
+			if(entry.initialized) {
+				if(entry.datatype == SDF_NUM) {
+					fprintf(csFile,"%s %d %.*e %s\n",entry.chname,1,precision,entry.data.chval,monitorstring);
 				} else {
-					encodeBURTString(myTable[ii].data.strval, burtString, sizeof(burtString));
-					fprintf(csFile,"%s %d %s %s\n",myTable[ii].chname,1,burtString,monitorstring);
+					encodeBURTString(entry.data.strval, burtString, sizeof(burtString));
+					fprintf(csFile,"%s %d %s %s\n",entry.chname,1,burtString,monitorstring);
 				}
 			}
 			break;
 		   case SDF_FILE_BURT_ONLY:
-			if(myTable[ii].datatype == SDF_NUM) {
-				fprintf(csFile,"%s %d %.*e\n",myTable[ii].chname,1,precision,myTable[ii].data.chval);
+			if(entry.datatype == SDF_NUM) {
+				fprintf(csFile,"%s %d %.*e\n",entry.chname,1,precision,entry.data.chval);
 			} else {
-				encodeBURTString(myTable[ii].data.strval, burtString, sizeof(burtString));
-				fprintf(csFile,"%s %d %s \n",myTable[ii].chname,1,burtString);
+				encodeBURTString(entry.data.strval, burtString, sizeof(burtString));
+				fprintf(csFile,"%s %d %s \n",entry.chname,1,burtString);
 			}
 			break;
 		   default:
@@ -1071,6 +1174,7 @@ int writeTable2File(char *burtdir,
 		}
 	}
 	fclose(csFile);
+	return true;
 }
 
 /// Function to append alarm settings to saved files..
@@ -1078,9 +1182,9 @@ int writeTable2File(char *burtdir,
 ///	@param[in] sdffile	Name of the file being saved.
 ///	@param[in] currentload	Base file name of the associated alarms.snap file..
 int appendAlarms2File(
-		char *sdfdir,
-		char *sdffile,
-		char *currentload
+		const char *sdfdir,
+		const char *sdffile,
+		const char *currentload
 		)
 {
 char sdffilename[256];
@@ -1123,23 +1227,23 @@ int lderror = 0;
 /// Routine used to decode and handle BURT save requests.
 int savesdffile(int saveType, 		///< Save file format definition.
 		int saveOpts, 		///< Save file options.
-		char *sdfdir, 		///< Directory to save file in.
-		char *model, 		///< Name of the model used to build file name.
-		char *currentfile, 	///< Name of file last read (Used if option is to overwrite).
-		char *saveasfile,	///< Name of file to be saved.
-		char *currentload,	///< Name of file, less directory info.
-		dbAddr sfaddr,		///< Address of EPICS channel to write save file name.
-		dbAddr staddr,		///< Address of EPICS channel to write save file time.
-		dbAddr rladdr)
+		const char *sdfdir, 		///< Directory to save file in.
+		const char *model, 		///< Name of the model used to build file name.
+		const char *currentfile, 	///< Name of file last read (Used if option is to overwrite).
+		const char *saveasfile,	///< Name of file to be saved.
+		const char *currentload,	///< Name of file, less directory info.
+		epics::DBEntry<epics::PVType::String>& save_file_entry,		///< Address of EPICS channel to write save file name.
+		epics::DBEntry<epics::PVType::String>& save_time_entry,		///< Address of EPICS channel to write save file time.
+		epics::DBEntry<epics::PVType::String>& reload_time_entry)
 {
 char filename[256];
 char ftype[16];
 int status;
 char filemsg[128];
-char timestring[64];
-char shortfilename[64];
+embedded::fixed_string<256> timestring{};
+embedded::fixed_string<256> shortfilename{};
 
-	time_t now = time(NULL);
+	time_t now = time(nullptr);
 	struct tm *mytime  = localtime(&now);
 
 	switch(saveOpts)
@@ -1147,29 +1251,29 @@ char shortfilename[64];
 		case SAVE_TIME_NOW:
 			sprintf(filename,"%s%s_%d%02d%02d_%02d%02d%02d.snap", sdfdir,currentload,
 			(mytime->tm_year - 100),  (mytime->tm_mon + 1),  mytime->tm_mday,  mytime->tm_hour,  mytime->tm_min,  mytime->tm_sec);
-			sprintf(shortfilename,"%s_%d%02d%02d_%02d%02d%02d", currentload,
+            shortfilename.printf("%s_%d%02d%02d_%02d%02d%02d", currentload,
 			(mytime->tm_year - 100),  (mytime->tm_mon + 1),  mytime->tm_mday,  mytime->tm_hour,  mytime->tm_min,  mytime->tm_sec);
 			// printf("File to save is TIME NOW: %s\n",filename);
 			break;
 		case SAVE_OVERWRITE:
 			sprintf(filename,"%s",currentfile);
-			sprintf(shortfilename,"%s",currentload);
+			shortfilename.printf("%s",currentload);
 			// printf("File to save is OVERWRITE: %s\n",filename);
 			break;
 		case SAVE_BACKUP:
 			sprintf(filename,"%s%s_%d%02d%02d_%02d%02d%02d.snap",sdfdir,currentload,
 			(mytime->tm_year - 100),  (mytime->tm_mon + 1),  mytime->tm_mday,  mytime->tm_hour,  mytime->tm_min,  mytime->tm_sec);
-			sprintf(shortfilename,"%s",currentload);
+			shortfilename.printf("%s",currentload);
 			// printf("File to save is BACKUP: %s\n",filename);
 			break;
 		case SAVE_OVERWRITE_TABLE:
 			sprintf(filename,"%s%s.snap",sdfdir,currentload);
-			sprintf(shortfilename,"%s",currentload);
+			shortfilename.printf("%s",currentload);
 			// printf("File to save is BACKUP OVERWRITE: %s\n",filename);
 			break;
 		case SAVE_AS:
 			sprintf(filename,"%s%s.snap",sdfdir,saveasfile);
-			sprintf(shortfilename,"%s",saveasfile);
+			shortfilename.printf("%s",saveasfile);
 			// printf("File to save is SAVE_AS: %s\n",filename);
 			break;
 
@@ -1181,13 +1285,12 @@ char shortfilename[64];
 	{
 		case SAVE_TABLE_AS_SDF:
 			// printf("Save table as sdf\n");
-			status = writeTable2File(sdfdir,filename,SDF_FILE_PARAMS_ONLY,cdTable);
-			if(status != 0) {
+			if(!writeTable2File(sdfdir,filename,SDF_FILE_PARAMS_ONLY,cdTable)) {
                             sprintf(filemsg,"FAILED FILE SAVE %s",filename);
 			    logFileEntry(filemsg);
                             return(-2);
 			}
-			status = appendAlarms2File(sdfdir,shortfilename,currentload);
+			status = appendAlarms2File(sdfdir,shortfilename.c_str(),currentload);
 			if(status != 0) {
                             sprintf(filemsg,"FAILED To Append Alarms -  %s",currentload);
 			    logFileEntry(filemsg);
@@ -1197,9 +1300,8 @@ char shortfilename[64];
                         break;
 		case SAVE_EPICS_AS_SDF:
 			// printf("Save epics as sdf\n");
-			getEpicsSettings(chNum, NULL);
-			status = writeTable2File(sdfdir,filename,SDF_FILE_PARAMS_ONLY,cdTableP);
-			if(status != 0) {
+			getEpicsSettings();
+			if(!writeTable2File(sdfdir,filename,SDF_FILE_PARAMS_ONLY,cdTableP)) {
                             sprintf(filemsg,"FAILED EPICS SAVE %s",filename);
 			    logFileEntry(filemsg);
                             return(-2);
@@ -1212,12 +1314,14 @@ char shortfilename[64];
 			return(-1);
 	}
 	logFileEntry(filemsg);
-	getSdfTime(timestring, 128);
+	getSdfTime(timestring);
 	// printf(" Time of save = %s\n",timestring);
-	status = dbPutField(&sfaddr,DBR_STRING,shortfilename,1);
-	status = dbPutField(&staddr,DBR_STRING,timestring,1);
-	status = dbPutField(&rladdr,DBR_STRING,timestring,1);
+    save_file_entry.set(shortfilename);
+	save_time_entry.set(timestring);
+	reload_time_entry.set(timestring);
 	return(0);
+
+	// I can type above your caret
 }
 
 
@@ -1243,7 +1347,7 @@ void resetASG(char *name, int lock) {
 #endif
 
 /// Routine used to create local tables for reporting uninitialize and not monitored channels on request.
-int createSortTableEntries(int numEntries,int wcval,char *wcstring,int *noInit,time_t *times)
+int createSortTableEntries(int numEntries,int wcval,const char *wcstring,int *noInit,time_t *times)
 {
 int ii,jj;
 int notMon = 0;
@@ -1253,10 +1357,10 @@ int lnb = 0;	// line b - non mon chan list index
 #ifdef CA_SDF
 int lnc = 0;	// line c - disconnected chan list index
 #endif
-char tmpname[64];
+fixed_string<64> tmpname;
 time_t mtime=0;
 long nvals = 1;
-char liveset[64];
+fixed_string<64> liveset;
 double liveval = 0.0;
 
 	chNotInit = 0;
@@ -1271,14 +1375,13 @@ double liveval = 0.0;
 	for(ii=0;ii<fmNum;ii++) {
 		if(!filterTable[ii].init) chNotInit += 1;
 		if(filterTable[ii].init && !(filterTable[ii].mask & ALL_SWSTAT_BITS)) chNotMon += 1;
-		if(wcval  && (ret = strstr(filterTable[ii].fname,wcstring) == NULL)) {
+		if(wcval  && (ret = strstr(filterTable[ii].fname.c_str(),wcstring) == NULL)) {
 			continue;
 		}
-		bzero(tmpname,sizeof(tmpname));
-		strncpy(tmpname,filterTable[ii].fname,(strlen(filterTable[ii].fname)-1));
+		tmpname = filterTable[ii].fname;
 		if(!filterTable[ii].init) {
-			sprintf(uninitChans[lna].chname,"%s",tmpname);
-			strcpy(uninitChans[lna].burtset,"");
+			uninitChans[lna].chname.printf("%s",tmpname);
+			uninitChans[lna].burtset.clear();
 			uninitChans[lna].liveval = 0.0;
 			uninitChans[lna].sw[0] = (int)cdTableP[filterTable[ii].sw[0]].data.chval;
 			uninitChans[lna].sw[1] = (int)cdTableP[filterTable[ii].sw[1]].data.chval;
@@ -1288,7 +1391,8 @@ double liveval = 0.0;
 			lna ++;
 		}
 		if(filterTable[ii].init && !(filterTable[ii].mask & ALL_SWSTAT_BITS)) {
-			sprintf(unMonChans[lnb].chname,"%s",tmpname);
+
+		    unMonChans[lnb].chname = tmpname;
 			unMonChans[lnb].liveval = 0.0;
 			unMonChans[lnb].sw[0] = (int)cdTableP[filterTable[ii].sw[0]].data.chval;
 			unMonChans[lnb].sw[1] = (int)cdTableP[filterTable[ii].sw[1]].data.chval;
@@ -1314,65 +1418,54 @@ double liveval = 0.0;
 #ifdef CA_SDF
 		if(!cdTable[jj].connected) chDisconnected += 1;
 #endif
-		if(wcval  && (ret = strstr(cdTable[jj].chname,wcstring) == NULL)) {
+		if(wcval  && (ret = strstr(cdTable[jj].chname.c_str(),wcstring) == NULL)) {
 			continue;
 		}
 		// Uninitialized channels
 		if(!cdTable[jj].initialized && !cdTable[jj].filterswitch) {
 			// printf("Chan %s not init %d %d %d\n",cdTable[jj].chname,cdTable[jj].initialized,jj,numEntries);
 			if(lna < SDF_ERR_TSIZE) {
-				sprintf(uninitChans[lna].chname,"%s",cdTable[jj].chname);
+				uninitChans[lna].chname = cdTable[jj].chname;
 				if(cdTable[jj].datatype == SDF_NUM) {
 					liveval = cdTableP[jj].data.chval;
-					snprintf(liveset, sizeof(liveset),"%.10lf", liveval);
+					liveset.printf("%.10lf", liveval);
 
 				} else {
 					liveval = 0.0;
-					strncpy(liveset, cdTableP[jj].data.strval, sizeof(liveset));
-					liveset[sizeof(liveset)-1] = '\0';
+					liveset = cdTableP[jj].data.strval;
 				}
-				strncpy(uninitChans[lna].liveset, liveset, sizeof(uninitChans[lna].liveset));
-				uninitChans[lna].liveset[sizeof(uninitChans[lna].liveset)-1] = '\0';
+				uninitChans[lna].liveset = liveset;
 				uninitChans[lna].liveval = liveval;
 				uninitChans[lna].sigNum = jj;
 				uninitChans[lna].filtNum = -1;
 
-				if (times) {
-					snprintf(unMonChans[lna].timeset, sizeof(unMonChans[lna].timeset), "%s", (times ? ctime(&times[jj]) : " "));
-				} else {
-					sprintf(unMonChans[lna].timeset,"%s"," ");
-				}
+    		    unMonChans[lna].timeset = (times ? ctime(&times[jj]) : " ");
 
-				sprintf(uninitChans[lna].timeset,"%s"," ");
-				sprintf(uninitChans[lna].diff,"%s"," ");
-				lna ++;
+				uninitChans[lna].timeset = " ";
+				uninitChans[lna].diff = " ";
+				lna++;
 			}
 		}
 		// Unmonitored channels
 		if(cdTable[jj].initialized && !cdTable[jj].mask && !cdTable[jj].filterswitch) {
 			if(lnb < SDF_ERR_TSIZE) {
-				sprintf(unMonChans[lnb].chname,"%s",cdTable[jj].chname);
+				unMonChans[lnb].chname = cdTable[jj].chname;
 				if(cdTable[jj].datatype == SDF_NUM)
 				{
-					snprintf(unMonChans[lnb].burtset, sizeof(unMonChans[lnb].burtset),"%.10lf",cdTable[jj].data.chval);
-					snprintf(unMonChans[lnb].liveset, sizeof(unMonChans[lnb].liveset),"%.10lf",cdTableP[jj].data.chval);
+					unMonChans[lnb].burtset.printf("%.10lf",cdTable[jj].data.chval);
+					unMonChans[lnb].liveset.printf("%.10lf",cdTableP[jj].data.chval);
 					unMonChans[lnb].liveval = cdTableP[jj].data.chval;
 				} else {
-					sprintf(unMonChans[lnb].burtset,"%s",cdTable[jj].data.strval);
-					sprintf(unMonChans[lnb].liveset,"%s",cdTableP[jj].data.strval);
+					unMonChans[lnb].burtset = cdTable[jj].data.strval;
+					unMonChans[lnb].liveset = cdTableP[jj].data.strval;
 					unMonChans[lnb].liveval = 0.0;
 				}
 
                 unMonChans[lnb].sigNum = jj;
 				unMonChans[lnb].filtNum = -1;
 
-				if (times) {
-					snprintf(unMonChans[lnb].timeset, sizeof(unMonChans[lnb].timeset), "%s", (times ? ctime(&times[jj]) : " "));
-					sprintf(unMonChans[lnb].diff,"%s"," ");
-				} else {
-					sprintf(unMonChans[lnb].timeset,"%s"," ");
-					sprintf(unMonChans[lnb].diff,"%s"," ");
-				}
+                unMonChans[lnb].timeset = (times ? ctime(&times[jj]) : " ");
+                unMonChans[lnb].diff = " ";
 
 				lnb ++;
 			}
@@ -1380,25 +1473,22 @@ double liveval = 0.0;
 #ifdef CA_SDF
 		if(!cdTable[jj].connected && !cdTable[jj].filterswitch) {
 			if (lnc < SDF_ERR_TSIZE) {
-				sprintf(disconnectChans[lnc].chname, "%s", cdTable[jj].chname);
+				disconnectChans[lnc].chname = cdTable[jj].chname;
 				if(cdTable[jj].datatype == SDF_NUM) {
 					liveval = cdTableP[jj].data.chval;
-					snprintf(liveset, sizeof(liveset),"%.10lf", liveval);
+					liveset.printf("%.10lf", liveval);
 
 				} else {
 					liveval = 0.0;
-					strncpy(liveset, cdTableP[jj].data.strval, sizeof(liveset));
-					liveset[sizeof(liveset)-1] = '\0';
+					liveset = cdTableP[jj].data.strval;
 				}
-				strncpy(disconnectChans[lnc].liveset, liveset, sizeof(disconnectChans[lnc].liveset));
-				disconnectChans[lnc].liveset[sizeof(disconnectChans[lnc].liveset)-1] = '\0';
+				disconnectChans[lnc].liveset = liveset;
 				disconnectChans[lnc].liveval = liveval;
                 disconnectChans[lnc].sigNum = jj;
 				disconnectChans[lnc].filtNum = -1;
-				sprintf(disconnectChans[lnc].timeset,"%s"," ");
-				sprintf(disconnectChans[lnc].diff,"%s"," ");
-				lnc ++;
-
+				disconnectChans[lnc].timeset = " ";
+				disconnectChans[lnc].diff = " ";
+				lnc++;
 			}
 		}
 #endif
@@ -1406,24 +1496,24 @@ double liveval = 0.0;
 	// Clear out the uninit tables.
 	for(jj=lna;jj<(lna + 50);jj++)
 	{
-		sprintf(uninitChans[jj].chname,"%s"," ");
-		sprintf(uninitChans[jj].burtset,"%s"," ");
-		sprintf(uninitChans[jj].liveset,"%s"," ");
+		uninitChans[jj].chname = " ";
+		uninitChans[jj].burtset = " ";
+		uninitChans[jj].liveset = " ";
 		uninitChans[jj].liveval = 0.0;
-		sprintf(uninitChans[jj].timeset,"%s"," ");
-		sprintf(uninitChans[jj].diff,"%s"," ");
+		uninitChans[jj].timeset = " ";
+		uninitChans[jj].diff = " ";
         uninitChans[jj].sigNum = 0;         // is this the right value, should it be -1?
 		uninitChans[jj].filtNum = -1;
 	}
 	// Clear out the unmon tables.
 	for(jj=lnb;jj<(lnb + 50);jj++)
 	{
-		sprintf(unMonChans[jj].chname,"%s"," ");
-		sprintf(unMonChans[jj].burtset,"%s"," ");
-		sprintf(unMonChans[jj].liveset,"%s"," ");
+		unMonChans[jj].chname = " ";
+		unMonChans[jj].burtset = " ";
+		unMonChans[jj].liveset = " ";
 		unMonChans[jj].liveval = 0.0;
-		sprintf(unMonChans[jj].timeset,"%s"," ");
-		sprintf(unMonChans[jj].diff,"%s"," ");
+		unMonChans[jj].timeset = " ";
+		unMonChans[jj].diff = " ";
         unMonChans[jj].sigNum = 0;
 		unMonChans[jj].filtNum = -1;
 	}
@@ -1431,12 +1521,12 @@ double liveval = 0.0;
 	// Clear out the disconnected tables.
 	for(jj=lnc;jj<(lnc + 50);jj++)
 	{
-		sprintf(disconnectChans[jj].chname,"%s"," ");
-		sprintf(disconnectChans[jj].burtset,"%s"," ");
-		sprintf(disconnectChans[jj].liveset,"%s"," ");
+		disconnectChans[jj].chname = " ";
+		disconnectChans[jj].burtset = " ";
+		disconnectChans[jj].liveset = " ";
 		disconnectChans[jj].liveval = 0.0;
-		sprintf(disconnectChans[jj].timeset,"%s"," ");
-		sprintf(disconnectChans[jj].diff,"%s"," ");
+		disconnectChans[jj].timeset = " ";
+		disconnectChans[jj].diff = " ";
         disconnectChans[jj].sigNum = 0;
 		disconnectChans[jj].filtNum = -1;
 	}
@@ -1446,9 +1536,9 @@ double liveval = 0.0;
 }
 
 /// Common routine to load monitoring tables into EPICS channels for MEDM screen.
+template <std::size_t entry_count>
 int reportSetErrors(char *pref,			///< Channel name prefix from EPICS environment. 
-		     int numEntries, 			///< Number of entries in table to be reported.
-		     SET_ERR_TABLE setErrTable[],	///< Which table to report to EPICS channels.
+		     fixed_size_vector<SET_ERR_TABLE , entry_count>& setErrTable,	///< Which table to report to EPICS channels.
 		     int page,                      ///< Which page of 40 to display.
              int linkInFilters)				///< Should the SDF_FM_LINE values be set.
 {
@@ -1484,21 +1574,22 @@ int minusOne = -1;
 
 	// Get the page number to display
 	mypage = page;
-	// Calculat start index to the diff table.
+	// Calculate start index to the diff table.
 	myindex = page *  SDF_ERR_DSIZE;
+	int numEntries = setErrTable.size();
 	if(myindex == numEntries && numEntries > 0) {
 		mypage --;
 		myindex = mypage *  SDF_ERR_DSIZE;
 	}
 	// If index is > number of entries in the table, then page back.
-        if(myindex > numEntries) {
+    if(myindex > numEntries) {
 		mypage = numEntries / SDF_ERR_DSIZE;
 		myindex = mypage *  SDF_ERR_DSIZE;
-        }
+    }
 	// Set the stop index to the diff table.
 	rc = myindex + SDF_ERR_DSIZE;
 	// If stop index beyond last diff table entry, set it to last entry.
-        if(rc > numEntries) rc = numEntries;
+    if(rc > numEntries) rc = numEntries;
 	numDisp = rc - myindex;
 
 	// Fill in table entries.
@@ -1507,7 +1598,7 @@ int minusOne = -1;
 		sprintf(s, "%s_%s_STAT%d", pref,"SDF_SP", lineNum);
 		status = dbNameToAddr(s,&saddr);
 		//sprintf(stmp, "%s%s", (setErrTable[ii].filtNum >= 0 ? "* " : ""), setErrTable[ii].chname);
-		status = dbPutField(&saddr,DBR_UCHAR,&setErrTable[ii].chname,flength);
+		status = dbPutField(&saddr,DBR_UCHAR,setErrTable[ii].chname.c_str(),flength);
 
 		sprintf(s1, "%s_%s_STAT%d_BURT", pref,"SDF_SP", lineNum);
 		status = dbNameToAddr(s1,&baddr);
@@ -1542,7 +1633,7 @@ int minusOne = -1;
 	}
 
 	// Clear out empty table entries.
-	for(ii=numDisp;ii<40;ii++)
+	for(ii=numDisp;ii<SDF_ERR_DSIZE;ii++)
 	{
 		sprintf(s, "%s_%s_STAT%d", pref,"SDF_SP", ii);
 		status = dbNameToAddr(s,&saddr);
@@ -1591,21 +1682,22 @@ int minusOne = -1;
 //	- Present Value
 //	- Time the present setting was applied.
 /// Setpoint monitoring routine.
-int spChecker(int monitorAll, SET_ERR_TABLE setErrTable[],int wcVal, char *wcstring, int listAll, int *totalDiffs)
+template <std::size_t entry_count>
+int spChecker(int monitorAll, fixed_size_vector<SET_ERR_TABLE, entry_count>& setErrTable,int wcVal, const char *wcstring, int listAll, int *totalDiffs)
 {
    	int errCntr = 0;
 	ADDRESS paddr;
 	long status=0;
-	int ii;
+	//int ii;
 	double rval;
-	char sval[128];
+	fixed_string<128> sval;
 	time_t mtime;
-	char localtimestring[256];
+	fixed_string<256> localtimestring;
 	int localErr = 0;
-	char liveset[64];
-	char burtset[64];
-	char diffB2L[64];
-	char swName[64];
+	fixed_string<64> liveset;
+	fixed_string<64> burtset;
+	fixed_string<64> diffB2L;
+	fixed_string<64> swName;
 	double sdfdiff = 0.0;
 	double liveval = 0.0;
 	int filtDiffs=0;
@@ -1622,72 +1714,76 @@ int spChecker(int monitorAll, SET_ERR_TABLE setErrTable[],int wcVal, char *wcstr
 */
 #endif
 	// Check filter switch settings first
-	     errCntr = checkFilterSwitches(fmNum,setErrTable,monitorAll,listAll,wcVal,wcstring,&filtDiffs);
-	     *totalDiffs = filtDiffs;
-	     if(chNum) {
-		for(ii=0;ii<chNum;ii++) {
+        setErrTable.clear();
+	    errCntr = checkFilterSwitches(setErrTable,monitorAll,listAll,wcVal,wcstring,&filtDiffs);
+	    *totalDiffs = filtDiffs;
+
+	    int index = -1;
+	    for (auto& curSP: cdTable)
+        {
+	        ++index;
 			if((errCntr < SDF_ERR_TSIZE) && 		// Within table max size
-			  ((cdTable[ii].mask != 0) || (monitorAll)) && 	// Channel is to be monitored
-			   cdTable[ii].initialized && 			// Channel was set in BURT
-			   cdTable[ii].filterswitch == 0 ||		// Not a filter switch channel
-			   (listAll && cdTable[ii].filterswitch == 0))
+			  ((curSP.mask != 0) || (monitorAll)) && 	// Channel is to be monitored
+			   curSP.initialized && 			// Channel was set in BURT
+			   curSP.filterswitch == 0 ||		// Not a filter switch channel
+			   (listAll && curSP.filterswitch == 0))
 			{
 				localErr = 0;
 				mtime = 0;
 				// Find address of channel
-				status = GET_ADDRESS(cdTable[ii].chname,&paddr);
+				GET_ADDRESS(curSP.chname,&paddr);
 				// If this is a digital data type, then get as double.
-				if(cdTable[ii].datatype == SDF_NUM)
+				if(curSP.datatype == SDF_NUM)
 				{
-					status = GET_VALUE_NUM(paddr,&rval,&mtime, NULL); //&(cdTable[ii].connected));
-					if(cdTable[ii].data.chval != rval || listAll)
+					GET_VALUE_NUM(paddr,&rval,&mtime, NULL); //&(curSP.connected));
+					if(curSP.data.chval != rval || listAll)
 					{
-						sdfdiff = fabs(cdTable[ii].data.chval - rval);
-						snprintf(burtset,sizeof(burtset),"%.10lf",cdTable[ii].data.chval);
-						snprintf(liveset,sizeof(liveset),"%.10lf",rval);
+						sdfdiff = fabs(curSP.data.chval - rval);
+						burtset.printf("%.10lf",curSP.data.chval);
+						liveset.printf("%.10lf",rval);
 						liveval = rval;
-						snprintf(diffB2L,sizeof(diffB2L),"%.8le",sdfdiff);
+						diffB2L.printf("%.8le",sdfdiff);
 						localErr = 1;
 					}
 				// If this is a string type, then get as string.
 				} else {
-                    updateStrVarSetPoint(ii);
-					status = GET_VALUE_STR(paddr,sval,sizeof(sval),&mtime, NULL); //&(cdTable[ii].connected));
-					if(strcmp(cdTable[ii].data.strval,sval) != 0 || listAll)
+                    updateStrVarSetPoint(index);
+					GET_VALUE_STR(paddr,sval,&mtime, NULL); //&(curSP.connected));
+					if(sval != curSP.data.strval || listAll)
 					{
-						sprintf(burtset,"%s",cdTable[ii].data.strval);
-						sprintf(liveset,"%s",sval);
+						burtset = curSP.data.strval;
+						liveset = sval;
 						liveval = 0.0;
-						sprintf(diffB2L,"%s","                                   ");
+						diffB2L.printf("%s","                                   ");
 						localErr = 1;
 					}
 				}
 				if(localErr) *totalDiffs += 1;
-				if(localErr && wcVal  && (strstr(cdTable[ii].chname,wcstring) == NULL))
+				if(localErr && wcVal  && (strstr(curSP.chname.c_str(),wcstring) == NULL))
 					localErr = 0;
 				// If a diff was found, then write info the EPICS setpoint diff table.
 				if(localErr)
 				{
-					sprintf(setErrTable[errCntr].chname,"%s", cdTable[ii].chname);
-
-					sprintf(setErrTable[errCntr].burtset, "%s", burtset);
-
-					sprintf(setErrTable[errCntr].liveset, "%s", liveset);
-					setErrTable[errCntr].liveval = liveval;
-					sprintf(setErrTable[errCntr].diff, "%s", diffB2L);
-					setErrTable[errCntr].sigNum = ii;
-					setErrTable[errCntr].filtNum = -1;
-
-					strcpy(localtimestring, ctime(&mtime));
-					localtimestring[strlen(localtimestring) - 1] = 0;
-					sprintf(setErrTable[errCntr].timeset, "%s", localtimestring);
-					if(cdTable[ii].mask) setErrTable[errCntr].chFlag |= 1;
-					else setErrTable[errCntr].chFlag &= ~(1);
+				    setErrTable.emplace_back();
+				    auto& curErr = setErrTable.back();
+					curErr.chname = curSP.chname;
+
+					curErr.burtset = burtset;
+
+					curErr.liveset = liveset;
+					curErr.liveval = liveval;
+					curErr.diff = diffB2L;
+					curErr.sigNum = index;
+					curErr.filtNum = -1;
+
+					localtimestring = ctime(&mtime);
+					curErr.timeset = localtimestring;
+					if(curSP.mask) curErr.chFlag |= 1;
+					else curErr.chFlag &= ~(1);
 					errCntr ++;
 				}
 			}
 		}
-	     }
 	return(errCntr);
 }
 
@@ -1697,29 +1793,30 @@ int spChecker(int monitorAll, SET_ERR_TABLE setErrTable[],int wcVal, char *wcstr
 /// @param modTable[in] An array of SET_ERR_TABLE entries that hold the current views state to be merged with cdTable
 ///
 /// @remark modifies cdTable
-int modifyTable(int numEntries,SET_ERR_TABLE modTable[])
+int modifyTable(table_range modTable)
 {
-int ii,jj;
-int fmIndex = -1;
-unsigned int sn,sn1;
-int found = 0;
-	for(ii=0;ii<numEntries;ii++)
+    int jj;
+    int fmIndex = -1;
+    unsigned int sn,sn1;
+    int found = 0;
+
+	for (const auto& entry:modTable)
 	{
 		// if accept or monitor is set
-		if(modTable[ii].chFlag > 3) 
+		if(entry.chFlag > 3)
 		{
 			found = 0;
 			for(jj=0;jj<chNum;jj++)
 			{
-				if (strcmp(cdTable[jj].chname,modTable[ii].chname) == 0) {
-					if ( CHFLAG_ACCEPT_BIT(modTable[ii].chFlag) ) {
-						if(cdTable[jj].datatype == SDF_NUM) cdTable[jj].data.chval = modTable[ii].liveval;/* atof(modTable[ii].liveset);*/
-						else sprintf(cdTable[jj].data.strval,"%s",modTable[ii].liveset);
+				if (cdTable[jj].chname == entry.chname) {
+					if ( CHFLAG_ACCEPT_BIT(entry.chFlag) ) {
+						if(cdTable[jj].datatype == SDF_NUM) cdTable[jj].data.chval = entry.liveval;/* atof(entry.liveset);*/
+						else sprintf(cdTable[jj].data.strval,"%s",entry.liveset);
 						cdTable[jj].initialized = 1;
 						found = 1;
 						fmIndex = cdTable[jj].filterNum;
 					}
-					if(CHFLAG_MONITOR_BIT(modTable[ii].chFlag)) {
+					if(CHFLAG_MONITOR_BIT(entry.chFlag)) {
 						fmIndex = cdTable[jj].filterNum;
 						if (fmIndex >= 0 && fmIndex < SDF_MAX_FMSIZE) {
 							// filter module use the manualy modified state, cannot just toggle
@@ -1732,21 +1829,21 @@ int found = 0;
 					}
 				}
 			}
-			if(modTable[ii].filtNum >= 0 && !found) { 
-				fmIndex = modTable[ii].filtNum;
+			if(entry.filtNum >= 0 && !found) {
+				fmIndex = entry.filtNum;
 				// printf("This is a filter from diffs = %s\n",filterTable[fmIndex].fname);
 				filterTable[fmIndex].newSet = 1;
-				sn = modTable[ii].sigNum;
+				sn = entry.sigNum;
 				sn1 = sn / SDF_MAX_TSIZE;
 				sn %= SDF_MAX_TSIZE;
-				if(CHFLAG_ACCEPT_BIT(modTable[ii].chFlag)) {
-					cdTable[sn].data.chval = modTable[ii].sw[0];
+				if(CHFLAG_ACCEPT_BIT(entry.chFlag)) {
+					cdTable[sn].data.chval = entry.sw[0];
 					cdTable[sn].initialized = 1;
-					cdTable[sn1].data.chval = modTable[ii].sw[1];
+					cdTable[sn1].data.chval = entry.sw[1];
 					cdTable[sn1].initialized = 1;
 					filterTable[fmIndex].init = 1;
 				}
-				if(CHFLAG_MONITOR_BIT(modTable[ii].chFlag)) {
+				if(CHFLAG_MONITOR_BIT(entry.chFlag)) {
 					filterTable[fmIndex].mask = filterMasks[fmIndex];
 				 	cdTable[sn].mask = filterMasks[fmIndex];
 				 	cdTable[sn1].mask = filterMasks[fmIndex];
@@ -1754,33 +1851,33 @@ int found = 0;
 			}
 		}
 	}
-	newfilterstats(fmNum);
+	newfilterstats();
 	return(0);
 }
 
-int resetSelectedValues(int errNum, SET_ERR_TABLE modTable[])
+template <std::size_t entry_count>
+int resetSelectedValues(fixed_size_vector<SET_ERR_TABLE, entry_count>& modTable)
 {
 long status;
-int ii;
 int sn;
 int sn1 = 0;
 ADDRESS saddr;
 
-	for(ii=0;ii<errNum;ii++)
+	for(const auto& entry:modTable)
 	{
-		if (modTable[ii].chFlag & 2)
+		if (entry.chFlag & 2)
 		{
-			sn = modTable[ii].sigNum;
+			sn = entry.sigNum;
 			if(sn > SDF_MAX_TSIZE) {
 				sn1 = sn / SDF_MAX_TSIZE;
 				sn %= SDF_MAX_TSIZE;
 			}
-			status = GET_ADDRESS(cdTable[sn].chname,&saddr);
-			if (cdTable[sn].datatype == SDF_NUM) status = PUT_VALUE(saddr,SDF_NUM,&(cdTable[sn].data.chval));
-			else status = PUT_VALUE(saddr,SDF_STR,cdTable[sn].data.strval);;
+			GET_ADDRESS(cdTable[sn].chname,&saddr);
+			if (cdTable[sn].datatype == SDF_NUM) PUT_VALUE(saddr,SDF_NUM,&(cdTable[sn].data.chval));
+			else PUT_VALUE(saddr,SDF_STR,cdTable[sn].data.strval);;
 			if(sn1) {
-				status = GET_ADDRESS(cdTable[sn1].chname,&saddr);
-				status = PUT_VALUE(saddr,SDF_NUM,&(cdTable[sn1].data.chval));
+				GET_ADDRESS(cdTable[sn1].chname,&saddr);
+				PUT_VALUE(saddr,SDF_NUM,&(cdTable[sn1].data.chval));
 			}
 		}
 	}
@@ -1788,48 +1885,44 @@ ADDRESS saddr;
 }
 
 /// This function sets filter module request fields to aid in decoding errant filter module switch settings.
-void newfilterstats(int numchans) {
+void newfilterstats() {
 	ADDRESS paddr;
 	long status;
-	int ii;
+
 	FILE *log=0;
-	char chname[128];
-	int mask = 0x1ffff;
+	fixed_string<128> chname;
+	unsigned int mask = 0x1ffff;
 	int tmpreq;
 	int counter = 0;
 	int rsw1,rsw2;
 	unsigned int tmpL = 0;
 
 	printf("In newfilterstats\n");
-	for(ii=0;ii<numchans;ii++) {
-		if(filterTable[ii].newSet) {
+	for(auto& filterEntry:filterTable) {
+		if(filterEntry.newSet) {
 			counter ++;
-			filterTable[ii].newSet = 0;
-			filterTable[ii].init = 1;
-			rsw1 = filterTable[ii].sw[0];
-			rsw2 = filterTable[ii].sw[1];
-			filterTable[ii].mask =  cdTable[rsw1].mask | cdTable[rsw2].mask;;
-			cdTable[rsw1].mask = filterTable[ii].mask;
-			cdTable[rsw2].mask = filterTable[ii].mask;
+			filterEntry.newSet = 0;
+			filterEntry.init = 1;
+			rsw1 = filterEntry.sw[0];
+			rsw2 = filterEntry.sw[1];
+			filterEntry.mask =  cdTable[rsw1].mask | cdTable[rsw2].mask;;
+			cdTable[rsw1].mask = filterEntry.mask;
+			cdTable[rsw2].mask = filterEntry.mask;
 			tmpreq =  ((unsigned int)cdTable[rsw1].data.chval & 0xffff) + 
 				(((unsigned int)cdTable[rsw2].data.chval & 0xffff) << 16);
-			filterTable[ii].swreq = filtCtrlBitConvert(tmpreq);
-			bzero(chname,sizeof(chname));
+			filterEntry.swreq = filtCtrlBitConvert(tmpreq);
 			// Find address of channel
-			strcpy(chname,filterTable[ii].fname);
-			strcat(chname,"SWREQ");
-			status = GET_ADDRESS(chname,&paddr);
-			if(!status) {
-				tmpL = (unsigned int)filterTable[ii].swreq;
-				status = PUT_VALUE_INT(paddr,&tmpL);
+			chname  = filterEntry.fname;
+			chname += "SWREQ";
+			if(GET_ADDRESS(chname,&paddr)) {
+				tmpL = (unsigned int)filterEntry.swreq;
+				PUT_VALUE_INT(paddr,&tmpL);
 			}
-			bzero(chname,sizeof(chname));
 			// Find address of channel
-			strcpy(chname,filterTable[ii].fname);
-			strcat(chname,"SWMASK");
-			status = GET_ADDRESS(chname,&paddr);
-			if(!status) {
-				status = PUT_VALUE_INT(paddr,(unsigned int*)&mask);
+			chname = filterEntry.fname;
+			chname += "SWMASK";
+			if(GET_ADDRESS(chname,&paddr)) {
+				PUT_VALUE_INT(paddr,&mask);
 			}
 			// printf("New filter %d %s = 0x%x\t0x%x\t0x%x\n",ii,filterTable[ii].fname,filterTable[ii].swreq,filterTable[ii].sw[0],filterTable[ii].sw[1]);
 		}
@@ -1838,55 +1931,43 @@ void newfilterstats(int numchans) {
 }
 
 /// This function writes BURT settings to EPICS records.
-int writeEpicsDb(int numchans,		///< Number of channels to write
-	         CDS_CD_TABLE myTable[],	///< Table with data to be written.
+template <std::size_t entry_count>
+int writeEpicsDb(fixed_size_vector<CDS_CD_TABLE, entry_count>& myTable,	///< Table with data to be written.
 	     	 int command) 		///< Write request.
 {
 	ADDRESS paddr;
 	long status;
 	int ii;
 
-	// chNotFound = 0;
 	switch (command)
 	{
 		case SDF_LOAD_DB_ONLY:
 		case SDF_LOAD_PARTIAL:
-			for(ii=0;ii<numchans;ii++) {
-				// Find address of channel
-				status = GET_ADDRESS(myTable[ii].chname,&paddr);
-				if (!status)
-				{
-					if (myTable[ii].datatype == SDF_NUM)
-					{
-						status = PUT_VALUE(paddr,SDF_NUM,&(myTable[ii].data.chval));
-					} else {
-						status = PUT_VALUE(paddr,SDF_STR,myTable[ii].data.strval);
-					}
-				}
-				else {				// Write errors to chan not found table.
-				printf("CNF for %s = %d\n",myTable[ii].chname,status);
-				#if 0
-					if(chNotFound < SDF_ERR_TSIZE) {
-						sprintf(unknownChans[chNotFound].chname,"%s",myTable[ii].chname);
-						sprintf(unknownChans[chNotFound].liveset,"%s"," ");
-						unknownChans[chNotFound].liveval = 0.0;
-						sprintf(unknownChans[chNotFound].timeset,"%s"," ");
-						sprintf(unknownChans[chNotFound].diff,"%s"," ");
-						unknownChans[chNotFound].chFlag = 0;
-					}
-					chNotFound ++;
-				#endif
-				}
-			}
-
-                        // Set the SDF_FILE_LOADED environment var to 1,
-                        // signalling to the epipcs sequencer to
-                        // trigger a BURT_RESTORE
 
+		    for (const auto& entry:myTable)
+		    {
+                        // Find address of channel
+                        if (GET_ADDRESS(entry.chname,&paddr))
                         {
-                            set_sdf_file_loaded(1);
+                            if (entry.datatype == SDF_NUM)
+                            {
+                                PUT_VALUE(paddr,SDF_NUM,&(entry.data.chval));
+                            } else {
+                                PUT_VALUE(paddr,SDF_STR,entry.data.strval);
+                            }
                         }
-			break;
+                        else {				// Write errors to chan not found table.
+                            printf("CNF for %s = %d\n",entry.chname,status);
+                        }
+                    }
+                    // Set the SDF_FILE_LOADED environment var to 1,
+                    // signalling to the epipcs sequencer to
+                    // trigger a BURT_RESTORE
+
+                    {
+                        set_sdf_file_loaded(1);
+                    }
+                    break;
 		case SDF_READ_ONLY:
 			// If request was only to re-read the BURT file, then don't want to apply new settings.
 			// This is typically the case where only mask fields were changed in the BURT file to
@@ -1895,24 +1976,24 @@ int writeEpicsDb(int numchans,		///< Number of channels to write
 			break;
 		case SDF_RESET:
 			// Only want to set those channels marked by a mask back to their original BURT setting.
-			for(ii=0;ii<numchans;ii++) {
-				// FIXME: check does this make sense w/ a bitmask in mask?
-				// can filter modules get here?  It seems that they will, so would we need
-				// to mask the value we set ?
-			    if(myTable[ii].mask) {
-			    	//Find address of channel
-			    	status = GET_ADDRESS(myTable[ii].chname,&paddr);
-			    	if (!status)
-			    	{
-			    		if (myTable[ii].datatype == SDF_NUM)
-						{
-							status = PUT_VALUE(paddr,SDF_NUM,&(myTable[ii].data.chval));
-						} else {
-							status = PUT_VALUE(paddr,SDF_STR,myTable[ii].data.strval);
-						}
-			    	}
-			    }
-			}
+			for (const auto& entry: myTable)
+			{
+                // FIXME: check does this make sense w/ a bitmask in mask?
+                // can filter modules get here?  It seems that they will, so would we need
+                // to mask the value we set ?
+                if(entry.mask) {
+                    //Find address of channel
+                    if (GET_ADDRESS(entry.chname,&paddr))
+                    {
+                        if (entry.datatype == SDF_NUM)
+                        {
+                            PUT_VALUE(paddr,SDF_NUM,&(entry.data.chval));
+                        } else {
+                            PUT_VALUE(paddr,SDF_STR,entry.data.strval);
+                        }
+                    }
+                }
+            }
 			break;
 		default:
 			printf("writeEpicsDb setting routine got unknown request \n");
@@ -1925,10 +2006,10 @@ int writeEpicsDb(int numchans,		///< Number of channels to write
 
 
 /// Function to read BURT files and load data into local tables.
-int readConfig( char *pref,		///< EPICS channel prefix from EPICS environment.
-		char *sdfile, 		///< Name of the file to read.
+int readConfig( const char *pref,		///< EPICS channel prefix from EPICS environment.
+		const char *sdfile, 		///< Name of the file to read.
 		int command,		///< Read file request type.
-		char *alarmfile)
+		const char *alarmfile)
 {
 	FILE *cdf=0;
 	FILE *adf=0;
@@ -1937,13 +2018,14 @@ int readConfig( char *pref,		///< EPICS channel prefix from EPICS environment.
 	int lock=0;
 	char s1[128],s2[128],s3[128],s4[128],s5[128],s6[128],s7[128],s8[128];
 	char ls[6][64];
-	dbAddr paddr;
+	ADDRESS paddr;
+	dbAddr reloadDbAddr;
 	long status=0;
 	int lderror = 0;
 	int ropts = 0;
 	int nvals = 1;
 	int starttime=0,totaltime=0;
-	char timestring[256];
+	embedded::fixed_string<256> timestring{};
 	char line[128];
 	char *fs=0;
 	char ifo[4];
@@ -1964,7 +2046,6 @@ int readConfig( char *pref,		///< EPICS channel prefix from EPICS environment.
     
 
 	s1[0]=s2[0]=s3[0]=s4[0]=s5[0]=s6[0]=s7[0]=s8[0]='\0';
-	timestring[0]='\0';
 	line[0]='\0';
 	ifo[0]='\0';
 	fname[0]=errMsg[0]='\0';
@@ -1974,10 +2055,10 @@ int readConfig( char *pref,		///< EPICS channel prefix from EPICS environment.
 	clock_gettime(CLOCK_REALTIME,&t);
 	starttime = t.tv_nsec;
 
-	getSdfTime(timestring, 256);
+	getSdfTime(timestring);
 
 	if(command == SDF_RESET) {
-		lderror = writeEpicsDb(chNum,cdTable,command);
+		lderror = writeEpicsDb(cdTable,command);
 	} else {
 		printf("PARTIAL %s\n",sdfile);
 		cdf = fopen(sdfile,"r");
@@ -2030,12 +2111,12 @@ int readConfig( char *pref,		///< EPICS channel prefix from EPICS environment.
 			argcount = parseLine(line,sizeof(s1),s1,s2,s3,s4,s5,s6);
 			if (strcmp(s4, "") == 0) strcpy(s4, "0");
 			if(argcount == -1) {
-				sprintf(readErrTable[rderror].chname,"%s", s1);
-				sprintf(readErrTable[rderror].burtset, "%s", "Improper quotations ");
-				sprintf(readErrTable[rderror].liveset, "Line # %d", lineCnt);
+				readErrTable[rderror].chname = s1;
+				readErrTable[rderror].burtset = "Improper quotations ";
+				readErrTable[rderror].liveset.printf("Line # %d", lineCnt);
 				readErrTable[rderror].liveval = 0.0;
-				sprintf(readErrTable[rderror].diff, "%s", sdfile);
-				sprintf(readErrTable[rderror].timeset, "%s", timestring);
+				readErrTable[rderror].diff = sdfile;
+				readErrTable[rderror].timeset = timestring;
 				rderror ++;
 				printf("Read error --- %s\n",s1);
 				continue;
@@ -2050,10 +2131,8 @@ int readConfig( char *pref,		///< EPICS channel prefix from EPICS environment.
 				strstr(s1,"_SWREQ") == NULL &&
 				argcount > 2)
 			{
-				// Clear out the local tabel channel name string.
-				bzero(cdTableP[chNumP].chname,strlen(cdTableP[chNumP].chname));
 				// Load channel name into local table.
-				strcpy(cdTableP[chNumP].chname,s1);
+				cdTableP[chNumP].chname = s1;
 				// Check if s4 (monitor or not) is set (0/1). If doesn/'t exist in file, set to zero in local table.
 				if(argcount > 3 && isdigit(s4[0])) {
 					// printf("%s %s %s %s\n",s1,s2,s3,s4);
@@ -2095,7 +2174,7 @@ int readConfig( char *pref,		///< EPICS channel prefix from EPICS environment.
 				   // Add settings to local table.
 				   for(ii=0;ii<chNum;ii++)
 				   {
-					if(strcmp(cdTable[ii].chname,cdTableP[chNumP].chname) == 0)
+					if(cdTable[ii].chname == cdTableP[chNumP].chname)
 					{
 					    saveStrSetPoint(ii, s3);
 						fmatch = 1;
@@ -2113,12 +2192,12 @@ int readConfig( char *pref,		///< EPICS channel prefix from EPICS environment.
 							cdTableP[chNumP].data.chval = atof(s3);
 							if(cdTable[ii].filterswitch) {
 								if(cdTableP[chNumP].data.chval > 0xffff) {
-									sprintf(readErrTable[rderror].chname,"%s", cdTable[ii].chname);
-									sprintf(readErrTable[rderror].burtset, "0x%x", (int)cdTableP[chNumP].data.chval);
-									sprintf(readErrTable[rderror].liveset, "%s", "OVERRANGE");
+									readErrTable[rderror].chname = cdTable[ii].chname;
+									readErrTable[rderror].burtset.printf("0x%x", (int)cdTableP[chNumP].data.chval);
+									readErrTable[rderror].liveset = "OVERRANGE";
 									readErrTable[rderror].liveval = 0.0;
-									sprintf(readErrTable[rderror].diff, "%s", "MAX VAL = 0xffff");
-									sprintf(readErrTable[rderror].timeset, "%s", timestring);
+									readErrTable[rderror].diff = "MAX VAL = 0xffff";
+									readErrTable[rderror].timeset = timestring;
 									printf("Read error --- %s\n", cdTable[ii].chname);
 									rderror ++;
 								}
@@ -2145,7 +2224,7 @@ int readConfig( char *pref,		///< EPICS channel prefix from EPICS environment.
 					strncpy(fname,s1,(strlen(s1)-4));
 				   	for(ii=0;ii<fmNum;ii++)
 				   	{
-						if(strcmp(filterTable[ii].fname,fname) == 0) 
+						if(filterTable[ii].fname == fname)
 						{
 							fmIndex = ii;
 							filterTable[fmIndex].newSet = 1;
@@ -2158,17 +2237,15 @@ int readConfig( char *pref,		///< EPICS channel prefix from EPICS environment.
 				} else {
 					// printf("CNF for %s \n",cdTableP[chNumP].chname);
 					if(chNotFound < SDF_ERR_TSIZE) {
-						sprintf(unknownChans[chNotFound].chname,"%s",cdTableP[chNumP].chname);
-                                                ADDRESS tmpaddr;
-						status = GET_ADDRESS(cdTableP[chNumP].chname, &tmpaddr);
-						if(!status) {
-							sprintf(unknownChans[chNotFound].liveset,"%s","RO Channel ");
+						unknownChans[chNotFound].chname = cdTableP[chNumP].chname;
+						if(GET_ADDRESS(cdTableP[chNumP].chname,&paddr)) {
+							unknownChans[chNotFound].liveset  = "RO Channel ";
 						} else {
-							sprintf(unknownChans[chNotFound].liveset,"%s","  ");
+							unknownChans[chNotFound].liveset = "  ";
 						}
 						unknownChans[chNotFound].liveval = 0.0;
-						sprintf(unknownChans[chNotFound].timeset,"%s"," ");
-						sprintf(unknownChans[chNotFound].diff,"%s"," ");
+						unknownChans[chNotFound].timeset = " ";
+						unknownChans[chNotFound].diff = " ";
 						unknownChans[chNotFound].chFlag = 0;
 					}
 					chNotFound ++;
@@ -2187,9 +2264,9 @@ int readConfig( char *pref,		///< EPICS channel prefix from EPICS environment.
 		fclose(cdf);
 		fclose(adf);
 		printf("Loading epics %d\n",chNumP);
-		lderror = writeEpicsDb(chNumP,cdTableP,command);
+		lderror = writeEpicsDb(cdTableP,command);
 		sleep(2);
-		newfilterstats(fmNum);
+		newfilterstats();
 		fmtInit = 1;
 	}
 
@@ -2203,8 +2280,8 @@ int readConfig( char *pref,		///< EPICS channel prefix from EPICS environment.
 		sprintf(errMsg,"New SDF request (No table update): %s\nFile = %s\nTotal Chans = %d with load time = %d usec\n",timestring,sdfile,chNumP,(totaltime/1000));
 	}
 	logFileEntry(errMsg);
-	status = dbNameToAddr(reloadtimechannel,&paddr);
-	status = dbPutField(&paddr,DBR_STRING,timestring,1);
+	status = dbNameToAddr(reloadtimechannel,&reloadDbAddr);
+	status = dbPutField(&reloadDbAddr,DBR_STRING,timestring.c_str(),1);
 	printf("Number of read errors = %d\n",rderror);
 	if(rderror) lderror = 2;
 	return(lderror);
@@ -2212,25 +2289,27 @@ int readConfig( char *pref,		///< EPICS channel prefix from EPICS environment.
 
 void registerFilters() {
 	int ii = 0;
-	char tmpstr[64];
+	fixed_string<64> tmpstr;
 	int amatch = 0, jj = 0;
 	fmNum = 0;
 	for(ii=0;ii<chNum;ii++) {
 		if(cdTable[ii].filterswitch == 1)
 		{
-			strncpy(filterTable[fmNum].fname,cdTable[ii].chname,(strlen(cdTable[ii].chname)-4));
-			snprintf(tmpstr, sizeof(tmpstr),"%s%s",filterTable[fmNum].fname,"SW2S");
+			filterTable[fmNum].fname = cdTable[ii].chname;
+            filterTable[fmNum].fname.pop_back_n(4);
+			tmpstr = filterTable[fmNum].fname;
+			tmpstr += "SW2S";
 			filterTable[fmNum].sw[0] = ii;
 			cdTable[ii].filterNum = fmNum;
 			amatch = 0;
 			for(jj=0;jj<chNum;jj++) {
-				if(strcmp(tmpstr,cdTable[jj].chname) == 0)
+				if(tmpstr == cdTable[jj].chname)
 				{
 					filterTable[fmNum].sw[1] = jj;
 					amatch = 1;
 				}
 			}
-			if(!amatch) printf("No match for %s\n",tmpstr);
+			if(!amatch) printf("No match for %s\n",tmpstr.c_str());
 			fmNum ++;
 		}
 	}
@@ -2239,40 +2318,50 @@ void registerFilters() {
 
 /// Provide initial values to the filterMasks arrray and the SDF_FM_MASK and SDF_FM_MASK_CTRL variables
 ///
-void setupFMArrays(char *pref, int *fmMasks, dbAddr *fmMaskAddr, dbAddr *fmCtrlAddr) {
+void setupFMArrays(char *pref,fixed_size_vector<int, SDF_MAX_FMSIZE>& fmMasks,
+                   fixed_size_vector<dbAddr, SDF_MAX_FMSIZE>& fmMaskAddr,
+                   fixed_size_vector<dbAddr, SDF_MAX_FMSIZE>& fmCtrlAddr) {
 	int ii = 0;
 	int sw1 = 0;
 	int sw2 = 0;
-	long status = 0;
-	char name[256];
-	char ctrl[256];
+	fixed_string<256> name;
+	fixed_string<256> ctrl;
 	int zero = 0;
 
-	for (ii = 0; ii < SDF_MAX_FMSIZE; ++ii) {
-		sw1 = filterTable[ii].sw[0];
-		sw2 = filterTable[ii].sw[1];
-		fmMasks[ii] = filterTable[ii].mask = cdTable[sw1].mask | cdTable[sw2].mask;
+	fmMasks.clear();
+	fmMaskAddr.clear();
+	fmCtrlAddr.clear();
+	for (ii = 0; ii < filterTable.size(); ++ii)
+	{
+        sw1 = filterTable[ii].sw[0];
+        sw2 = filterTable[ii].sw[1];
+        fmMasks[ii] = filterTable[ii].mask = cdTable[sw1].mask | cdTable[sw2].mask;
 
-		sprintf(name, "%s_SDF_FM_MASK_%d", pref, ii);
-		status = dbNameToAddr(name, &fmMaskAddr[ii]);
-		status = dbPutField(&fmMaskAddr[ii],DBR_LONG,&(fmMasks[ii]),1);
+        name.printf("%s_SDF_FM_MASK_%d", pref, ii);
+        fmMaskAddr.emplace_back();
+        dbNameToAddr(name.c_str(), &fmMaskAddr[ii]);
+        dbPutField(&fmMaskAddr[ii],DBR_LONG,&(fmMasks[ii]),1);
 
-		sprintf(ctrl, "%s_SDF_FM_MASK_CTRL_%d", pref, ii);
-		status = dbNameToAddr(ctrl, &fmCtrlAddr[ii]);
-		status = dbPutField(&fmCtrlAddr[ii],DBR_LONG,&zero,1);
+        ctrl.printf("%s_SDF_FM_MASK_CTRL_%d", pref, ii);
+        fmCtrlAddr.emplace_back();
+        dbNameToAddr(ctrl.c_str(), &fmCtrlAddr[ii]);
+        dbPutField(&fmCtrlAddr[ii],DBR_LONG,&zero,1);
 
-	} 
+    }
 }
 
 /// Copy the filter mask information from cdTable into fmMasks
 ///
-void resyncFMArrays(int *fmMasks, dbAddr *fmMaskAddr) {
+void resyncFMArrays(fixed_size_vector<int, SDF_MAX_FMSIZE>&fmMasks,
+                    fixed_size_vector<dbAddr, SDF_MAX_FMSIZE>& fmMaskAddr)
+{
 	int ii = 0;
 	int sw1 = 0;
 	int sw2 = 0;
 	long status = 0;
 
-	for (ii = 0; ii < SDF_MAX_FMSIZE; ++ii) {
+
+	for (ii = 0; ii < filterTable.size(); ++ii) {
 		sw1 = filterTable[ii].sw[0];
 		sw2 = filterTable[ii].sw[1];
 		fmMasks[ii] = filterTable[ii].mask = (cdTable[sw1].mask | cdTable[sw2].mask) & ALL_SWSTAT_BITS;
@@ -2296,7 +2385,11 @@ void resyncFMArrays(int *fmMasks, dbAddr *fmMaskAddr) {
 ///
 /// @remark Each of the input arrays is expected to have a length of SDF_MAX_FMSIZE
 /// with fmNum in actual use.
-void processFMChanCommands(int *fMask, dbAddr *fmMaskAddr, dbAddr *fmCtrlAddr, int *selectCounter, int errCnt, SET_ERR_TABLE *setErrTable) {
+void processFMChanCommands(
+        fixed_size_vector<int, SDF_MAX_FMSIZE>& fMask,
+        fixed_size_vector<dbAddr, SDF_MAX_FMSIZE>& fmMaskAddr,
+        fixed_size_vector<dbAddr, SDF_MAX_FMSIZE>& fmCtrlAddr,
+        int *selectCounter, table_range setErrTable) {
 	int ii = 0;
 	int jj = 0;
 	int refMask = 0;
@@ -2308,7 +2401,8 @@ void processFMChanCommands(int *fMask, dbAddr *fmMaskAddr, dbAddr *fmCtrlAddr, i
 	long nvals = 1;
 	int foundCh = 0;
 
-	for (ii = 0; ii < SDF_MAX_FMSIZE && ii < fmNum; ++ii) {
+	//std::distance(setErrTable.begin(), setErrTable.end());
+	for (ii = 0; ii < filterTable.size(); ++ii) {
 		status = dbGetField(&fmCtrlAddr[ii], DBR_LONG, &ctrl, &ropts, &nvals, NULL);
 
 		// only do work if there is a change
@@ -2325,10 +2419,17 @@ void processFMChanCommands(int *fMask, dbAddr *fmMaskAddr, dbAddr *fmCtrlAddr, i
 		foundCh = 0;
 		/* if there is a change, update4 the selection count) */
 		if (differsPre != differsPost) {
-			for (jj = 0; jj < errCnt && setErrTable[jj].filtNum >= 0 && setErrTable[jj].filtNum != ii; ++jj) {}
-			if (jj < errCnt && setErrTable[jj].filtNum == ii) {
-				setErrTable[jj].chFlag ^= CHFLAG_MONITOR_BIT_VAL;
-				foundCh = setErrTable[jj].chFlag;
+		    // search through setErrTable
+			//for (jj = 0; jj < errCnt && setErrTable[jj].filtNum >= 0 && setErrTable[jj].filtNum != ii; ++jj) {}
+			auto match = std::find_if(setErrTable.begin(), setErrTable.end(), [ii](const SET_ERR_TABLE& cur) -> bool {
+			    return cur.filtNum == ii;
+			});
+			// act on search
+			//if (jj < errCnt && setErrTable[jj].filtNum == ii)
+			if ( match != setErrTable.end())
+			{
+				match->chFlag ^= CHFLAG_MONITOR_BIT_VAL;
+				foundCh = match->chFlag;
 				selectCounter[2] += ( differsPre ? -1 : 1 );
 			}
 		}
@@ -2394,53 +2495,53 @@ long countDisconnectedChans()
 
 void nullCACallback(struct event_handler_args args) {}
 
-int getCAIndex(char *entry, ADDRESS *addr) {
+bool getCAIndex(const char *entry, ADDRESS *addr) {
 	int ii = 0;
 
-	if (!entry || !addr) return 1;
+	if (!entry || !addr) return false;
 	for (ii = 0; ii < chNum; ++ii) {
-		if (strcmp(cdTable[ii].chname, entry) == 0) {
+		if (cdTable[ii].chname == entry) {
 			*addr = ii;
-			return 0;
+			return true;
 		}
 	}
 	*addr = -1;
-	return 1;
+	return false;
 }
 
-int setCAValue(ADDRESS ii, int type, void *data)
+bool setCAValue(ADDRESS ii, SDF_TYPE type, const void *data)
 {
+	bool result = false;
 	int status = ECA_NORMAL;
-	int result = 1;
-	
+
 	if (ii >= 0 && ii < chNum) {
 		if (type == SDF_NUM) {
 			status = ca_put_callback(DBR_DOUBLE, caTable[ii].chanid, (double *)data, nullCACallback, NULL);
 		} else {
 			status = ca_put_callback(DBR_STRING, caTable[ii].chanid, (char *)data, nullCACallback, NULL);
 		}
-		result = (status == ECA_NORMAL ? 0 : 1);
+		result = (status == ECA_NORMAL);
 	}
 	return result;
 }
 
-int setCAValueEPICSLong(ADDRESS ii, unsigned int *data) {
+bool setCAValueLong(ADDRESS ii, const PV_INT_TYPE *data) {
 	double tmp = 0.0;
 
-	if (!data) return 1;
+	if (!data) return false;
 	tmp = (double)*data;
-	return setCAValue(ii,SDF_NUM,(void*)&tmp);
+	return setCAValue(ii,SDF_NUM,(const void*)&tmp);
 }
 
-int syncEpicsDoubleValue(int index, double *dest, time_t *tp, int *connp) {
+bool syncEpicsDoubleValue(ADDRESS index, double *dest, time_t *tp, int *connp) {
 	int debug = 0;
-	if (!dest || index < 0 || index >= chNum) return 1;
+	if (!dest || index < 0 || index >= chNum) return false;
 #if VERBOSE_DEBUG
 	if (strcmp(cdTable[caTable[index].chanIndex].chname, TEST_CHAN) == 0) {
 		debug=1;
 	}
 #endif
-	pthread_mutex_lock(&caTableMutex);
+	lock_guard l_(caTableMutex);
 	if (caTable[index].datatype == SDF_NUM) {
 		*dest = caTable[index].data.chval;
 		if (tp) {
@@ -2450,24 +2551,23 @@ int syncEpicsDoubleValue(int index, double *dest, time_t *tp, int *connp) {
 			*connp = caTable[index].connected;
 		}
 	}
-	pthread_mutex_unlock(&caTableMutex);
-	return 0;
+	return true;
 }
 
-int syncEpicsIntValue(ADDRESS index, unsigned int *dest, time_t *tp, int *connp) {
+bool syncEpicsIntValue(ADDRESS index, PV_INT_TYPE *dest, time_t *tp, int *connp) {
+	bool result = true;
 	double tmp = 0.0;
-	int result = 0;
 
-	if (!dest) return 1;
+	if (!dest) return false;
 	result = syncEpicsDoubleValue(index, &tmp, tp, connp);
-	*dest = (unsigned int)(tmp);
+	*dest = (PV_INT_TYPE)(tmp);
 	return result;
 }
 
-int syncEpicsStrValue(int index, char *dest, int dest_size, time_t *tp, int *connp) {
-	if (!dest || index < 0 || index >= chNum || dest_size < 1) return 1;
+bool syncEpicsStrValue(ADDRESS index, char *dest, int dest_size, time_t *tp, int *connp) {
+	if (!dest || index < 0 || index >= chNum || dest_size < 1) return false;
 	dest[0] = '\0';
-	pthread_mutex_lock(&caTableMutex);
+	lock_guard l_(caTableMutex);
 	if (caTable[index].datatype == SDF_STR) {
 		const int MAX_STR_LEN = sizeof(caTable[index].data.strval);
 		strncpy(dest, caTable[index].data.strval, (dest_size < MAX_STR_LEN ? dest_size : MAX_STR_LEN));
@@ -2479,14 +2579,13 @@ int syncEpicsStrValue(int index, char *dest, int dest_size, time_t *tp, int *con
 			*connp = caTable[index].connected;
 		}
 	}
-	pthread_mutex_unlock(&caTableMutex);
-	return 0;
+	return true;
 }
 
 /// Routine to handle subscription callbacks
 void subscriptionHandler(struct event_handler_args args) {
 	float val = 0.0;
-	EPICS_CA_TABLE *entry = args.usr;
+	EPICS_CA_TABLE *entry = reinterpret_cast<EPICS_CA_TABLE*>(args.usr);
 	EPICS_CA_TABLE *origEntry = entry;
 	int initialRedirIndex = 0;
 
@@ -2494,7 +2593,7 @@ void subscriptionHandler(struct event_handler_args args) {
 		return;
 	}
 
-	pthread_mutex_lock(&caTableMutex);
+	lock_guard l_(caTableMutex);
 
 	// If this entry has just reconnected, then do not write the old copy, write to the temporary/redir dest
 	initialRedirIndex = entry->redirIndex;
@@ -2549,7 +2648,6 @@ void subscriptionHandler(struct event_handler_args args) {
 		// The dbr_gr_enum type does not have time information, so we use current time
 		entry->mod_time = time(NULL);
 	}
-	pthread_mutex_unlock(&caTableMutex);
 }
 
 void connectCallback(struct connection_handler_args args) {
@@ -2576,65 +2674,68 @@ void connectCallback(struct connection_handler_args args) {
 			}
 		}
 
-		pthread_mutex_lock(&caTableMutex);
-		if (args.op == CA_OP_CONN_UP) {
-			// connection
-			if (is_enum) {
-				entry->redirIndex = entry->chanIndex + SDF_MAX_TSIZE;
-				connEntry = &(caConnEnumTable[entry->chanIndex]);
-			} else {
-				// reserve a new conn table entry if one is not already reserved
-				if (entry->redirIndex < 0) {
-					entry->redirIndex = chConnNum;
-					++chConnNum;
-				}
-				connEntry = &(caConnTable[entry->redirIndex]);
-			}
-			entry->chanid = args.chid;
+        {
+            lock_guard l_(caTableMutex);
+            if (args.op == CA_OP_CONN_UP) {
+                // connection
+                if (is_enum) {
+                    entry->redirIndex = entry->chanIndex + SDF_MAX_TSIZE;
+                    connEntry = &(caConnEnumTable[entry->chanIndex]);
+                } else {
+                    // reserve a new conn table entry if one is not already reserved
+                    if (entry->redirIndex < 0) {
+                        entry->redirIndex = chConnNum;
+                        ++chConnNum;
+                    }
+                    connEntry = &(caConnTable[entry->redirIndex]);
+                }
+                entry->chanid = args.chid;
 
-			// now copy items over to the connection record
-			connEntry->redirIndex = -1;
+                // now copy items over to the connection record
+                connEntry->redirIndex = -1;
 
-			connEntry->datatype = sdf_type;
-			connEntry->data.chval = 0.0;
-			entry->connected = 1;
-			connEntry->connected = 1;
-			connEntry->mod_time = 0;
-			connEntry->chanid = args.chid;
-			connEntry->chanIndex = entry->chanIndex;
-			typeChange = (connEntry->datatype != entry->datatype);
+                connEntry->datatype = sdf_type;
+                connEntry->data.chval = 0.0;
+                entry->connected = 1;
+                connEntry->connected = 1;
+                connEntry->mod_time = 0;
+                connEntry->chanid = args.chid;
+                connEntry->chanIndex = entry->chanIndex;
+                typeChange = (connEntry->datatype != entry->datatype);
 
-		} else {
-			// disconnect
-			entry->connected = 0;
-		}
-		pthread_mutex_unlock(&caTableMutex);
+            } else {
+                // disconnect
+                entry->connected = 0;
+            }
+        }
 
-		// now register/clear the subscription callback
-		pthread_mutex_lock(&caEvidMutex);
-		if (args.op == CA_OP_CONN_UP) {
-			// connect
-			// if we are subscribed but the types are wrong, then unsubscribe
-			if (caEvid[chanIndex] && typeChange) {
-				ca_clear_subscription(caEvid[chanIndex]);
-				caEvid[chanIndex] = 0;
-			}
-			// if we are not subscribed become subscribed
-			if (!caEvid[chanIndex]) {
-				chtype subtype = (sdf_type == SDF_NUM ? DBR_TIME_DOUBLE : DBR_TIME_STRING);
-				if (is_enum) {
-					subtype = DBR_GR_ENUM;
-				}
-				ca_create_subscription(subtype, 0, args.chid, DBE_VALUE, subscriptionHandler, entry, &(caEvid[chanIndex]));
-			}
-		} else {
-			// disconnect
-			if (caEvid[chanIndex]) {
-				ca_clear_subscription(caEvid[chanIndex]);
-				caEvid[chanIndex] = 0;
-			}
-		}
-		pthread_mutex_unlock(&caEvidMutex);
+        {
+            // now register/clear the subscription callback
+            lock_guard l_(caEvidMutex);
+            if (args.op == CA_OP_CONN_UP) {
+                // connect
+                // if we are subscribed but the types are wrong, then unsubscribe
+                if (caEvid[chanIndex] && typeChange) {
+                    ca_clear_subscription(caEvid[chanIndex]);
+                    caEvid[chanIndex] = 0;
+                }
+                // if we are not subscribed become subscribed
+                if (!caEvid[chanIndex]) {
+                    chtype subtype = (sdf_type == SDF_NUM ? DBR_TIME_DOUBLE : DBR_TIME_STRING);
+                    if (is_enum) {
+                        subtype = DBR_GR_ENUM;
+                    }
+                    ca_create_subscription(subtype, 0, args.chid, DBE_VALUE, subscriptionHandler, entry,
+                                           &(caEvid[chanIndex]));
+                }
+            } else {
+                // disconnect
+                if (caEvid[chanIndex]) {
+                    ca_clear_subscription(caEvid[chanIndex]);
+                    caEvid[chanIndex] = 0;
+                }
+            }
+        }
 	}
 }
 
@@ -2649,19 +2750,19 @@ void registerPV(char *PVname)
 		return;
 	}
 	//printf("Registering %s\n", PVname);
-	pthread_mutex_lock(&caTableMutex);
-	caTable[chNum].datatype = SDF_NUM;
-	caTable[chNum].connected = 0;
-	strncpy(cdTable[chNum].chname, PVname, 128);
-	cdTable[chNum].chname[128-1] = '\0';
-	cdTable[chNum].datatype = SDF_NUM;
-	cdTable[chNum].initialized = 0;
-	cdTable[chNum].filterswitch = 0;
-	cdTable[chNum].filterNum = -1;
-	cdTable[chNum].error = 0;
-	cdTable[chNum].initialized = 0;
-	cdTable[chNum].mask = 0;
-	pthread_mutex_unlock(&caTableMutex);
+    {
+        lock_guard l_(caTableMutex);
+        caTable[chNum].datatype = SDF_NUM;
+        caTable[chNum].connected = 0;
+        cdTable[chNum].chname = PVname;
+        cdTable[chNum].datatype = SDF_NUM;
+        cdTable[chNum].initialized = 0;
+        cdTable[chNum].filterswitch = 0;
+        cdTable[chNum].filterNum = -1;
+        cdTable[chNum].error = 0;
+        cdTable[chNum].initialized = 0;
+        cdTable[chNum].mask = 0;
+    }
 
 	status = ca_create_channel(PVname, connectCallback, &(caTable[chNum]), 0, &chid1);
 
@@ -2724,7 +2825,7 @@ void parseChannelListReq(char *fname) {
 		if (strstr(s1,"_SWMASK") != NULL ||
 			strstr(s1,"_SDF_NAME") != NULL ||
 			strstr(s1,"_SWREQ") != NULL) continue;
-		if (isAlarmChannel(s1)) continue;
+		if (isAlarmChannelRaw(s1)) continue;
 		registerPV(s1);
 	}
 	fclose(f);
@@ -2732,18 +2833,14 @@ void parseChannelListReq(char *fname) {
 
 /// Routine to get the state of the CA Thread
 int getCAThreadState() {
-	int state = 0;
-	pthread_mutex_lock(&caStateMutex);
-	state = caThreadState;
-	pthread_mutex_unlock(&caStateMutex);
-	return state;
+	lock_guard l_(caStateMutex);
+	return caThreadState;
 }
 
 /// Routine to set the state of the CA Thread
 void setCAThreadState(int state) {
-	pthread_mutex_lock(&caStateMutex);
+	lock_guard l_(caStateMutex);
 	caThreadState = state;
-	pthread_mutex_unlock(&caStateMutex);
 }
 
 // copy the given entry (which should be in caConnTable or caConnEnumTable) to caTable
@@ -2779,7 +2876,7 @@ void syncCAConnections(long *disconnected_count)
 	int chanIndex = SDF_MAX_TSIZE;
 	chtype subtype = DBR_TIME_DOUBLE;
 
-	pthread_mutex_lock(&caTableMutex);
+	lock_guard l_(caTableMutex);
 	// sync all non-enum channel connections that have taken place
 	for (ii = 0; ii < chConnNum; ++ii) {
 		copyConnectedCAEntry(&(caConnTable[ii]));
@@ -2817,7 +2914,6 @@ void syncCAConnections(long *disconnected_count)
 	//	}
 	//	*disconnected_count = tmp;
 	//}
-	pthread_mutex_unlock(&caTableMutex);
 }
 
 /// Main loop of the CA Thread
@@ -2835,6 +2931,7 @@ void *caMainLoop(void *param)
 	registerFilters();
 	printf("Done with parsing, CA thread continuing\n");
 	setCAThreadState(CA_STATE_RUNNING);
+	return nullptr;
 }
 
 /// Routine used to read in all channels to monitor from an INI file and create local connections
@@ -2864,7 +2961,7 @@ void setupCASDF()
 		caTable[ii].datatype = SDF_NUM;
 		caTable[ii].data.chval = 0.0;
 		caTable[ii].connected = 0;
-		caTable[ii].chanid = (void *)-1;
+		caTable[ii].chanid = nullptr;
 		caTable[ii].mod_time = (time_t)0;
 		caTable[ii].chanIndex = ii;
 
@@ -2872,7 +2969,7 @@ void setupCASDF()
 		caConnTable[ii].datatype = SDF_NUM;
 		caConnTable[ii].data.chval = 0.0;
 		caConnTable[ii].connected = 0;
-		caConnTable[ii].chanid = (void *)-1;
+		caConnTable[ii].chanid = nullptr;
 		caConnTable[ii].mod_time = (time_t)0;
 		caConnTable[ii].chanIndex = 0;
 
@@ -2880,7 +2977,7 @@ void setupCASDF()
 		caConnEnumTable[ii].datatype = SDF_UNKNOWN;
 		caConnEnumTable[ii].data.chval = 0.0;
 		caConnEnumTable[ii].connected = 0;
-		caConnEnumTable[ii].chanid = (void *)-1;
+		caConnEnumTable[ii].chanid = nullptr;
 		caConnEnumTable[ii].mod_time = (time_t)0;
 		caConnEnumTable[ii].chanIndex = ii;
 
@@ -2906,20 +3003,13 @@ void setupCASDF()
 		bzero((void *)&(readErrTable[ii]), sizeof(readErrTable[ii]));
 	}
 	droppedPVCount = 0;
-	pthread_mutex_init(&caStateMutex, NULL);	// FIXME check for errors
-	pthread_mutex_init(&caTableMutex, NULL);
-	pthread_mutex_init(&caEvidMutex, NULL);
 }
 
 /// Routine to tear-down mutexes and other resources used by the CA SDF system
 void cleanupCASDF()
 {
-	pthread_mutex_destroy(&caEvidMutex);
-	pthread_mutex_destroy(&caTableMutex);
-	pthread_mutex_destroy(&caStateMutex);
 }
 #else
-
 void countDisconnectedChannels(int curTable)
 {
 }
@@ -2932,17 +3022,18 @@ void updateStrVarSetPoint(int index)
 {
 }
 
-int getDbValueDouble(ADDRESS *paddr,double *dest,time_t *tp) {
+bool getDbValueDouble(ADDRESS *paddr,double *dest,time_t *tp) {
+
 	struct buffer {
 		DBRtime
 		double dval;
 	} buffer;
 	long options = DBR_TIME;
 	long nvals = 1;
-	int result = 1;
+	bool result = false;
 
 	if (dest && paddr) {
-		result = dbGetField(paddr, DBR_DOUBLE, &buffer, &options, &nvals, NULL);
+		result = dbGetField(paddr, DBR_DOUBLE, &buffer, &options, &nvals, NULL) == 0;
 		*dest = buffer.dval;
 		if (tp) {
 			*tp = buffer.time.secPastEpoch + POSIX_TIME_AT_EPICS_EPOCH;
@@ -2950,17 +3041,17 @@ int getDbValueDouble(ADDRESS *paddr,double *dest,time_t *tp) {
 	}
 	return result;
 }
-int getDbValueLong(ADDRESS *paddr,unsigned int *dest,time_t *tp) {
+bool getDbValueLong(ADDRESS *paddr,PV_INT_TYPE *dest,time_t *tp) {
 	struct buffer {
 		DBRtime
 		unsigned int dval;
 	} buffer;
 	long options = DBR_TIME;
 	long nvals = 1;
-	int result = 1;
+	bool result = false;
 
 	if (dest && paddr) {
-		result = dbGetField(paddr, DBR_LONG, &buffer, &options, &nvals, NULL);
+		result = dbGetField(paddr, DBR_LONG, &buffer, &options, &nvals, NULL) == 0;
 		*dest = buffer.dval;
 		if (tp) {
 			*tp = buffer.time.secPastEpoch + POSIX_TIME_AT_EPICS_EPOCH;
@@ -2968,16 +3059,16 @@ int getDbValueLong(ADDRESS *paddr,unsigned int *dest,time_t *tp) {
 	}
 	return result;
 }
-int getDbValueString(ADDRESS *paddr,char *dest, int max_len, time_t *tp) {
+bool getDbValueString(ADDRESS *paddr,char *dest, int max_len, time_t *tp) {
 	struct buffer {
 		DBRtime
 		char sval[128];
 	} strbuffer;
 	long options = DBR_TIME;
 	long nvals = 1;
-	int result = 1;
+	bool result = false;
 	if (dest && paddr && (max_len > 0)) {
-		result = dbGetField(paddr,DBR_STRING,&strbuffer,&options,&nvals,NULL);
+		result = dbGetField(paddr,DBR_STRING,&strbuffer,&options,&nvals,NULL) == 0;
 		strncpy(dest, strbuffer.sval, max_len);
 		dest[max_len-1]='\0';
 		if (tp) {
@@ -3025,9 +3116,9 @@ void dbDumpRecords(DBBASE *pdbbase, const char *pref)
                 printf("\n  Alias:%s\n",dbGetRecordName(pdbentry));
             } else {
 		//fprintf(stderr, "processing %s\n", dbGetRecordName(pdbentry));
-		sprintf(cdTable[chNum].chname,"%s",dbGetRecordName(pdbentry));
+		cdTable[chNum].chname = dbGetRecordName(pdbentry);
 		// do not monitor the the SDF mask channels, they are part of this IOC
-		if (strncmp(cdTable[chNum].chname, mask_pref, pref_len)==0) {
+		if ( cdTable[chNum].chname == mask_pref ) {
             --cnt;
             continue;
 		}
@@ -3035,12 +3126,12 @@ void dbDumpRecords(DBBASE *pdbbase, const char *pref)
 		cdTable[chNum].filterNum = -1;
 		// Check if this is a filter module
 		// If so, initialize parameters
-		if((strstr(cdTable[chNum].chname,"_SW1S") != NULL) && (strstr(cdTable[chNum].chname,"_SW1S.") == NULL))
+		if((strstr(cdTable[chNum].chname.c_str(),"_SW1S") != NULL) && (strstr(cdTable[chNum].chname.c_str(),"_SW1S.") == NULL))
 		{
 			cdTable[chNum].filterswitch = 1;
 			fmNum ++;
 		}
-		if((strstr(cdTable[chNum].chname,"_SW2S") != NULL) && (strstr(cdTable[chNum].chname,"_SW2S.") == NULL))
+		if((strstr(cdTable[chNum].chname.c_str(),"_SW2S") != NULL) && (strstr(cdTable[chNum].chname.c_str(),"_SW2S.") == NULL))
 		{
 			cdTable[chNum].filterswitch = 2;
 		}
@@ -3094,42 +3185,6 @@ void listLocalRecords(DBBASE *pdbbase) {
 /// Called on EPICS startup; This is generic EPICS provided function, modified for LIGO use.
 int main(int argc,char *argv[])
 {
-	// Addresses for SDF EPICS records.
-	dbAddr reload_addr;
-	dbAddr sdfname_addr;
-	dbAddr reloadstat_addr;
-	dbAddr loadedfile_addr;
-	dbAddr sperroraddr;
-	dbAddr alrmchcountaddr;
-#ifdef CA_SDF
-	dbAddr disconnectcountaddr;
-	dbAddr droppedcountaddr;
-#endif
-	dbAddr filesetcntaddr;
-	dbAddr fulldbcntaddr;
-	dbAddr monchancntaddr;
-	dbAddr tablesortreqaddr;
-	dbAddr wcreqaddr;
-	dbAddr chnotfoundaddr;
-	dbAddr chnotinitaddr;
-	dbAddr sorttableentriesaddr;
-	dbAddr monflagaddr;
-	dbAddr reloadtimeaddr;
-	dbAddr rcgversion_addr;
-	dbAddr msgstraddr;
-	dbAddr edbloadedaddr;
-	dbAddr savecmdaddr;
-	dbAddr saveasaddr;
-	dbAddr wcstringaddr;
-	dbAddr savetypeaddr;
-	dbAddr saveoptsaddr;
-	dbAddr savefileaddr;
-	dbAddr savetimeaddr;
-	dbAddr daqmsgaddr;
-	dbAddr coeffmsgaddr;
-	dbAddr resetoneaddr;
-	dbAddr selectaddr[4];
-	dbAddr pagelockaddr[3];	// why is this an array of 3.  It looks like we can make this a single value
 #ifdef CA_SDF
 	// CA_SDF does not do a partial load on startup.
 	int sdfReq = SDF_READ_ONLY;
@@ -3143,8 +3198,8 @@ int main(int argc,char *argv[])
 	long nvals = 1;
 	int rdstatus = 0;
 	int burtstatus = 0;
-	char loadedSdf[256];
-	char sdffileloaded[256];
+	embedded::fixed_string<256> loadedSdf{};
+	embedded::fixed_string<256> sdffileloaded{};
    	int sperror = 0;
 	int noMon = 0;
 	int noInit = 0;
@@ -3157,9 +3212,9 @@ int main(int argc,char *argv[])
 	int monFlag = 0;
 	int sdfSaveReq = 0;
 	int saveType = 0;
-	char saveTypeString[64];
+	embedded::fixed_string<256> saveTypeString{};
 	int saveOpts = 0;
-	char saveOptsString[64];
+    embedded::fixed_string<256> saveOptsString{};
 	int fivesectimer = 0;
 	long daqFileCrc = 0;
 	long coeffFileCrc = 0;
@@ -3183,18 +3238,13 @@ int main(int argc,char *argv[])
 	int selectAll = 0;
 	int freezeTable = 0;
 	int zero = 0;
-	char backupName[64];
+	embedded::fixed_string<256> backupName{};
 	int lastTable = 0;
 	int cdSort = 0;
 	int diffCnt = 0;
-    	char errMsg[128];
+  	char errMsg[128];
 
-    loadedSdf[0] = '\0';
-    sdffileloaded[0] = '\0';
     tsrString[0] = '\0';
-    saveTypeString[0] = '\0';
-    saveOptsString[0] = '\0';
-    backupName[0] = '\0';
     errMsg[0] = '\0';
 
     if(argc>=2) {
@@ -3224,24 +3274,23 @@ int main(int argc,char *argv[])
 	char *fotonDiffFile = getenv("FOTON_DIFF_FILE");
 	char *logdir = getenv("LOG_DIR");
 	char myDiffCmd[256];
-	SET_ERR_TABLE *currentTable = 0;
-	int currentTableCnt = 0;
+	table_range currentTable;
+
 
 	if(stat(logdir, &st) == -1) mkdir(logdir,0777);
 	// strcat(sdf,"_safe");
-	char sdf[256];
-	char sdfile[256];
-	char sdalarmfile[256];
-	char bufile[256];
-	char saveasfilename[128];
-	char wcstring[64];
-	
-    strncpy(sdf, sdfenv, sizeof(sdf));
-    sdf[sizeof(sdf)-1] = '\0';
+	embedded::fixed_string<256> sdf;
+    embedded::fixed_string<256>  sdfile;
+	embedded::fixed_string<256>  sdalarmfile;
+	embedded::fixed_string<256>  bufile;
+    embedded::fixed_string<256>  saveasfilename;
+    embedded::fixed_string<256>  wcstring;
+
+    sdf = sdfenv;
 	
 	printf("My prefix is %s\n",pref);
-	sprintf(sdfile, "%s%s%s", sdfDir, sdf,".snap");					// Initialize with BURT_safe.snap
-	sprintf(bufile, "%s%s", sdfDir, "fec.snap");					// Initialize table dump file
+	sdfile.printf("%s%s%s", sdfDir, sdf.c_str(), ".snap");  // Initialize with BURT_safe.snap
+	bufile.printf("%s%s", sdfDir, "fec.snap");					// Initialize table dump file
 	sprintf(logfilename, "%s%s", logdir, "/ioc.log");					// Initialize table dump file
 	printf("SDF FILE = %s\n",sdfile);
 	printf("CURRENt FILE = %s\n",bufile);
@@ -3253,136 +3302,76 @@ int main(int argc,char *argv[])
 	int myreleased = RCG_VERSION_REL;
 	double myversion;
 
-	SETUP;
-	// listLocalRecords(*iocshPpdbbase);
-	myversion = majorversion + 0.1 * subversion1 + 0.01 * subversion2;
-	if(!myreleased) myversion *= -1.0;
-	char rcgversionname[256]; sprintf(rcgversionname, "%s_%s", pref, "RCG_VERSION");	// Set RCG Version EPICS
-	status = dbNameToAddr(rcgversionname,&rcgversion_addr);
-    if( status )
-    {
-        fprintf(stderr, "*\nFATAL: The address for the %s variable could not be "
-                        "located in the EPICS database. \n", rcgversionname);
-        fprintf(stderr, "Failure with first lookup in the EPICS database, "
-                        "this usually signifies an error with the .cmd passed to iocsh(), "
-                        "or a misconfigured EPCS environment.\n*\n");
-        return -1;
-    }
-	status = dbPutField(&rcgversion_addr,DBR_DOUBLE,&myversion,1);
+    auto common_name = [pref](const char* name) -> epics::channel_name {
+        return epics::make_name(pref, name);
+    };
 
-	// Create BURT/SDF EPICS channel names
-	char reloadChan[256]; sprintf(reloadChan, "%s_%s", pref, "SDF_RELOAD");		// Request to load new BURT
-	// Set request to load safe.snap on startup
-	status = dbNameToAddr(reloadChan,&reload_addr);
-	status = dbPutField(&reload_addr,DBR_LONG,&sdfReq,1);		// Init request for startup.
 
-	char reloadStat[256]; sprintf(reloadStat, "%s_%s", pref, "SDF_RELOAD_STATUS");	// Status of last reload
-	status = dbNameToAddr(reloadStat,&reloadstat_addr);
-	status = dbPutField(&reloadstat_addr,DBR_LONG,&rdstatus,1);	// Init to zero.
+	SETUP();
+#ifndef USE_SYSTEM_TIME
+        timechannel = std::make_unique< epics::DBEntry< epics::PVType::String > >( common_name("TIME_STRING") );
+#endif
+	// listLocalRecords(*iocshPpdbbase);
+	myversion = majorversion + 0.1 * subversion1 + 0.01 * subversion2;
+	if(!myreleased)
+        {
+            myversion *= -1.0;
+        }
+	epics::DBEntry< epics::PVType::Float64 > rcgversion_channel(common_name("RCG_VERSION"), myversion);
+        rcgversion_channel.set( myversion );
 
-	char sdfFileName[256]; sprintf(sdfFileName, "%s_%s", pref, "SDF_NAME");		// Name of file to load next request
-	// Initialize BURT file to be loaded next request = safe.snap
-	status = dbNameToAddr(sdfFileName,&sdfname_addr);		// Get Address
-	status = dbPutField(&sdfname_addr,DBR_STRING,sdf,1);		// Init to safe.snap
 
-	char loadedFile[256]; sprintf(loadedFile, "%s_%s", pref, "SDF_LOADED");		// Name of file presently loaded
-	status = dbNameToAddr(loadedFile,&loadedfile_addr);		//Get Address
+	// Create BURT/SDF EPICS channel names
 
-	char edbloadedFile[256]; sprintf(edbloadedFile, "%s_%s", pref, "SDF_LOADED_EDB");	// Name of file presently loaded
-	status = dbNameToAddr(edbloadedFile,&edbloadedaddr);		// Get Address
+    epics::DBEntry< epics::PVType::Int32 > reload_channel(common_name("SDF_RELOAD"), sdfReq); // Init request for startup.
+    epics::DBEntry< epics::PVType::Int32 > reload_stat( common_name("SDF_RELOAD_STATUS"), rdstatus); // Init to zero
 
-	char speStat[256]; sprintf(speStat, "%s_%s", pref, "SDF_DIFF_CNT");		// Setpoint diff counter
-	status = dbNameToAddr(speStat,&sperroraddr);			// Get Address
-	status = dbPutField(&sperroraddr,DBR_LONG,&sperror,1);		// Init to zero.
+    // Initialize BURT file to be loaded next request = safe.snap
+    epics::DBEntry<epics::PVType::String> sdfname_channel(common_name("SDF_NAME"), sdf);
 
-	char spaStat[256]; sprintf(spaStat, "%s_%s", pref, "SDF_ALARM_CNT");		// Number of alarm settings in a BURT file.
-	status = dbNameToAddr(spaStat,&alrmchcountaddr);		// Get Address
-	status = dbPutField(&alrmchcountaddr,DBR_LONG,&alarmCnt,1);	// Init to zero.
+    epics::DBEntry<epics::PVType::String> loadedfile_channel(common_name("SDF_LOADED"));
 
-	char fcc[256]; sprintf(fcc, "%s_%s", pref, "SDF_FULL_CNT");			// Number of setting channels in EPICS db
-	status = dbNameToAddr(fcc,&fulldbcntaddr);
+    epics::DBEntry< epics::PVType::String > edbloaded_channel(common_name("SDF_LOADED_EDB"));	// Name of file presently loaded
 
-	char fsc[256]; sprintf(fsc, "%s_%s", pref, "SDF_FILE_SET_CNT");			// Number of settings inBURT file
-	status = dbNameToAddr(fsc,&filesetcntaddr);
+	epics::DBEntry< epics::PVType::Int32 > sperror_channel(common_name("SDF_DIFF_CNT"), sperror); // Setpoint diff counter
+    epics::DBEntry< epics::PVType::Int32 > alrmchcount_channel(common_name("SDF_ALARM_CNT"), alarmCnt); // Number of alarm settings in a BURT file.
+    epics::DBEntry< epics::PVType::Int32 > fulldbcnt_channel(common_name("SDF_FULL_CNT"));// Number of setting channels in EPICS db
 
-	char mcc[256]; sprintf(mcc, "%s_%s", pref, "SDF_UNMON_CNT");			// Number of settings NOT being monitored.
-	status = dbNameToAddr(mcc,&monchancntaddr);
+    epics::DBEntry< epics::PVType::Int32 > filesetcnt_channel(common_name("SDF_FILE_SET_CNT")); // Number of settings inBURT file
+    epics::DBEntry< epics::PVType::Int32 > unmonchancnt_channel(common_name("SDF_UNMON_CNT")); // Number of settings NOT being monitored.
 
 #ifdef CA_SDF
-	char dsc[256]; sprintf(dsc, "%s_%s", pref, "SDF_DISCONNECTED_CNT");
-	status = dbNameToAddr(dsc,&disconnectcountaddr);
+    epics::DBEntry< epics::PVType::Int32 > disconnectcount_channel(common_name("SDF_DISCONNECTED_CNT"));
 
-	char dpdc[256]; sprintf(dpdc, "%s_%s", pref, "SDF_DROPPED_CNT");
-	status = dbNameToAddr(dpdc,&droppedcountaddr);
+    epics::DBEntry< epics::PVType::Int32 > droppedcount_channel(common_name("SDF_DROPPED_CNT"));
 #endif
 
-	char tsrname[256]; sprintf(tsrname, "%s_%s", pref, "SDF_SORT");			// SDF Table sorting request
-	status = dbNameToAddr(tsrname,&tablesortreqaddr);
-
-	char wcname[256]; sprintf(wcname, "%s_%s", pref, "SDF_WILDCARD");			// SDF Table sorting request
-	status = dbNameToAddr(wcname,&wcreqaddr);
-	status = dbPutField(&wcreqaddr,DBR_LONG,&zero,1);		// Init to zero.
-
-	char cnfname[256]; sprintf(cnfname, "%s_%s", pref, "SDF_DROP_CNT");		// Number of channels not found.
-	status = dbNameToAddr(cnfname,&chnotfoundaddr);
+    epics::DBEntry< epics::PVType::UInt16 > tablesortreq_channel(common_name("SDF_SORT"));// SDF Table sorting request
+    epics::DBEntry< epics::PVType::Int32 > wcreq_channel(common_name("SDF_WILDCARD"), 0);               // SDF Table sorting request
+    epics::DBEntry< epics::PVType::Int32 > chnotfound_channel(common_name("SDF_DROP_CNT")); // Number of channels not found.
+    epics::DBEntry< epics::PVType::Int32 > chnotinit_channel(common_name("SDF_UNINIT_CNT"));// Number of channels not initialized.
+    epics::DBEntry< epics::PVType::Int32 > sorttableentries_channel(common_name("SDF_TABLE_ENTRIES")); // Number of entries in an SDF reporting table.
+    epics::DBEntry< epics::PVType::Int32 > monflag_channel(common_name("SDF_MON_ALL"), rdstatus); // Request to monitor all channels.
+    epics::DBEntry< epics::PVType::Int32 > savecmd_channel(common_name("SDF_SAVE_CMD"), rdstatus);	// SDF Save command.
+    epics::DBEntry< epics::PVType::Int32 > pagelock_channel(common_name("SDF_TABLE_LOCK"), freezeTable); // SDF Save command.
 
-	char cniname[256]; sprintf(cniname, "%s_%s", pref, "SDF_UNINIT_CNT");		// Number of channels not initialized.
-	status = dbNameToAddr(cniname,&chnotinitaddr);
-
-	char stename[256]; sprintf(stename, "%s_%s", pref, "SDF_TABLE_ENTRIES");	// Number of entries in an SDF reporting table.
-	status = dbNameToAddr(stename,&sorttableentriesaddr);
-
-	char monflagname[256]; sprintf(monflagname, "%s_%s", pref, "SDF_MON_ALL");	// Request to monitor all channels.
-	status = dbNameToAddr(monflagname,&monflagaddr);		// Get Address.
-	status = dbPutField(&monflagaddr,DBR_LONG,&rdstatus,1);		// Init to zero.
-
-	char savecmdname[256]; sprintf(savecmdname, "%s_%s", pref, "SDF_SAVE_CMD");	// SDF Save command.
-	status = dbNameToAddr(savecmdname,&savecmdaddr);		// Get Address.
-	status = dbPutField(&savecmdaddr,DBR_LONG,&rdstatus,1);		// Init to zero.
-
-	char pagelockname[128]; sprintf(pagelockname, "%s_%s", pref, "SDF_TABLE_LOCK");	// SDF Save command.
-	status = dbNameToAddr(pagelockname,&(pagelockaddr[0]));		// Get Address.
-	status = dbPutField(&(pagelockaddr[0]),DBR_LONG,&freezeTable,1);		// Init to zero.
-
-	char saveasname[256]; sprintf(saveasname, "%s_%s", pref, "SDF_SAVE_AS_NAME");	// SDF Save as file name.
 	// Clear out the save as file name request
-	status = dbNameToAddr(saveasname,&saveasaddr);			// Get Address.
-	status = dbPutField(&saveasaddr,DBR_STRING,"default",1);	// Set as dummy 'default'
-
-	char wcstringname[256]; sprintf(wcstringname, "%s_%s", pref, "SDF_WC_STR");	// SDF Save as file name.
-	status = dbNameToAddr(wcstringname,&wcstringaddr);			// Get Address.
-	status = dbPutField(&wcstringaddr,DBR_STRING,"",1);		// Set as dummy 'default'
-
-	char savetypename[256]; sprintf(savetypename, "%s_%s", pref, "SDF_SAVE_TYPE");	// SDF Save file type.
-	status = dbNameToAddr(savetypename,&savetypeaddr);
-
-	char saveoptsname[256]; sprintf(saveoptsname, "%s_%s", pref, "SDF_SAVE_OPTS");	// SDF Save file options.
-	status = dbNameToAddr(saveoptsname,&saveoptsaddr);
+    epics::DBEntry< epics::PVType::String > saveas_channel(common_name("SDF_SAVE_AS_NAME"), epics::channel_name("default")); // Set as dummy 'default'
 
-	char savefilename[256]; sprintf(savefilename, "%s_%s", pref, "SDF_SAVE_FILE");	// SDF Name of last file saved.
-	status = dbNameToAddr(savefilename,&savefileaddr);
-	status = dbPutField(&savefileaddr,DBR_STRING,"",1);
+	epics::DBEntry< epics::PVType::String > wcstring_channel(common_name("SDF_WC_STR"), "");// SDF Save as file name.
 
-	char savetimename[256]; sprintf(savetimename, "%s_%s", pref, "SDF_SAVE_TIME");	// SDF Time of last file save.
-	status = dbNameToAddr(savetimename,&savetimeaddr);
-	status = dbPutField(&savetimeaddr,DBR_STRING,"",1);
+    epics::DBEntry< epics::PVType::String > savetype_channel( common_name("SDF_SAVE_TYPE"));// SDF Save file type.
+    epics::DBEntry< epics::PVType::String > saveopts_channel( common_name("SDF_SAVE_OPTS"));// SDF Save file options.
 
-	char moddaqfilemsg[256]; sprintf(moddaqfilemsg, "%s_%s", pref, "MSGDAQ");	// Record to write if DAQ file changed.
-	status = dbNameToAddr(moddaqfilemsg,&daqmsgaddr);
-
-	char modcoefffilemsg[128]; sprintf(modcoefffilemsg, "%s_%s", pref, "MSG2");	// Record to write if Coeff file changed.
-	status = dbNameToAddr(modcoefffilemsg,&coeffmsgaddr);
+    epics::DBEntry<epics::PVType::String> savefile_channel(common_name("SDF_SAVE_FILE"), ""); // SDF Name of last file saved.
+    epics::DBEntry<epics::PVType::String> savetime_channel( common_name("SDF_SAVE_TIME"), ""); // SDF Time of last file save.
+    epics::DBEntry< epics::PVType::String > daqmsg_channel(common_name("MSGDAQ"));// Record to write if DAQ file changed.
+    epics::DBEntry< epics::PVType::String > coeffmsg_channel(common_name("MSG2"));	// Record to write if Coeff file changed.
 
+    epics::DBEntry< epics::PVType::String > msgstr_channel(common_name("SDF_MSG_STR"), "");// SDF Time of last file save.
 	char msgstrname[128]; sprintf(msgstrname, "%s_%s", pref, "SDF_MSG_STR");	// SDF Time of last file save.
-	status = dbNameToAddr(msgstrname,&msgstraddr);
-	status = dbPutField(&msgstraddr,DBR_STRING,"",1);
 
-#ifndef USE_SYSTEM_TIME
-	sprintf(timechannel,"%s_%s", pref, "TIME_STRING");
-	// printf("timechannel = %s\n",timechannel);
-#endif
-	sprintf(reloadtimechannel,"%s_%s", pref, "SDF_RELOAD_TIME");			// Time of last BURT reload
-	status = dbNameToAddr(reloadtimechannel,&reloadtimeaddr);
+    epics::DBEntry<epics::PVType::String> reloadtime_channel(common_name("SDF_RELOAD_TIME"), ""); // Time of last BURT reload
 
 	int pageNum = 0;
 	int pageNumSet = 0;
@@ -3391,23 +3380,14 @@ int main(int argc,char *argv[])
         status = dbNameToAddr(pagereqname,&pagereqaddr);                // Get Address.
 
 	unsigned int resetNum = 0;
-	char resetOneName[256]; sprintf(resetOneName, "%s_%s", pref, "SDF_RESET_CHAN");	// SDF reset one value.
-	status = dbNameToAddr(resetOneName,&resetoneaddr);
-	status = dbPutField(&resetoneaddr,DBR_LONG,&resetNum,1);
-
-	char selectName[256]; 
-	sprintf(selectName, "%s_%s", pref, "SDF_SELECT_SUM0");	// SDF reset one value.
-	status = dbNameToAddr(selectName,&selectaddr[0]);
-	status = dbPutField(&selectaddr[0],DBR_LONG,&resetNum,1);
-	sprintf(selectName, "%s_%s", pref, "SDF_SELECT_SUM1");	// SDF reset one value.
-	status = dbNameToAddr(selectName,&selectaddr[1]);
-	status = dbPutField(&selectaddr[1],DBR_LONG,&resetNum,1);
-	sprintf(selectName, "%s_%s", pref, "SDF_SELECT_SUM2");	// SDF reset one value.
-	status = dbNameToAddr(selectName,&selectaddr[2]);
-	status = dbPutField(&selectaddr[2],DBR_LONG,&resetNum,1);
-	sprintf(selectName, "%s_%s", pref, "SDF_SELECT_ALL");	// SDF reset one value.
-	status = dbNameToAddr(selectName,&selectaddr[3]);
-	status = dbPutField(&selectaddr[3],DBR_LONG,&selectAll,1);
+    epics::DBEntry< epics::PVType::UInt32 > resetone_channel(common_name("SDF_RESET_CHAN"), resetNum);	// SDF reset one value.
+
+    std::array< epics::DBEntry< epics::PVType::Int32 >, 4> select_channel{
+            epics::DBEntry< epics::PVType::Int32 >(common_name("SDF_SELECT_SUM0"), 0),
+            epics::DBEntry< epics::PVType::Int32 >(common_name("SDF_SELECT_SUM1"), 0),
+            epics::DBEntry< epics::PVType::Int32 >(common_name("SDF_SELECT_SUM2"), 0),
+            epics::DBEntry< epics::PVType::Int32 >(common_name("SDF_SELECT_SELECT_ALL"), selectAll),
+    };
 
 	dbAddr confirmwordaddr;
 	char confirmwordname[64]; 
@@ -3420,7 +3400,7 @@ int main(int argc,char *argv[])
 		char *buffer=0;
 		char fname[]="/monitor.req";
 		int len = strlen(sdfDir)+strlen(fname)+1;
-		buffer = malloc(len);
+		buffer = (char*)malloc(len);
 		if (!buffer) {
 			fprintf(stderr, "Unable to allocate memory to hold the path to monitor.req, aborting!");
 			exit(1);
@@ -3451,10 +3431,10 @@ int main(int argc,char *argv[])
     status = checkFileMod( coeffFile, &coeffFileMt, 1 );
     status = checkFileMod( fotonFile, &fotonFileMt, 1 );
 
-	reportSetErrors(pref, 0,setErrTable,0,1);
+	reportSetErrors(pref,setErrTable,0,1);
 
 	sleep(1);       // Need to wait before first restore to allow sequencers time to do their initialization.
-	cdSort = spChecker(monFlag,cdTableList,wcVal,wcstring,1,&status);
+	cdSort = spChecker(monFlag,cdTableList,wcVal,wcstring.c_str(),1,&status);
 
 	// Start Infinite Loop 		*******************************************************************************
 	for(;;) {
@@ -3465,119 +3445,142 @@ int main(int argc,char *argv[])
 			// see bug 965
 			ca_poll();
 			syncCAConnections(NULL);
-			status = dbPutField(&disconnectcountaddr,DBR_LONG,&chDisconnectedCount,1);
-			status = dbPutField(&droppedcountaddr,DBR_LONG,&droppedPVCount,1); 
+            disconnectcount_channel.set(chDisconnectedCount);
+            droppedcount_channel.set(droppedPVCount);
 		}
 #endif
 		fivesectimer = (fivesectimer + 1) % 50;		// Increment 5 second timer for triggering CRC checks.
 		// Check for reload request
-		status = dbGetField(&reload_addr,DBR_LONG,&request,&ropts,&nvals,NULL);
+        reload_channel.get(request);
+
 		// Get BURT Read File Name
-		status = dbNameToAddr(sdfFileName,&sdfname_addr);
-		status = dbGetField(&sdfname_addr,DBR_STRING,sdf,&ropts,&nvals,NULL);
+        sdfname_channel.get(sdf);
+		////status = dbNameToAddr(sdfFileName,&sdfname_addr);
+		////status = dbGetField(&sdfname_addr,DBR_STRING,sdf,&ropts,&nvals,NULL);
 
 		//  Create full filename including directory and extension.
-		sprintf(sdfile, "%s%s%s", sdfDir, sdf,".snap");
-		sprintf(sdalarmfile, "%s%s%s", sdfDir, sdf,"_alarms.snap");
+        sdfile.printf("%s%s%s", sdfDir, sdf.c_str(), ".snap");
+        sdalarmfile.printf("%s%s%s", sdfDir, sdf.c_str(), "_alarms.snap");
+
 		// Check if file name != to one presently loaded
-		if(strcmp(sdf,loadedSdf) != 0) burtstatus |= 1;
-		else burtstatus &= ~(1);
-		if(burtstatus == 0)
-				status = dbPutField(&msgstraddr,DBR_STRING," ",1);
-		if(burtstatus == 1)
-				status = dbPutField(&msgstraddr,DBR_STRING,"New SDF File Pending",1);
-		if(burtstatus & 2)
-				status = dbPutField(&msgstraddr,DBR_STRING,"Read Error: Errant line(s) in file",1);
-		if(burtstatus & 4)
-				status = dbPutField(&msgstraddr,DBR_STRING,"Read Error: File Not Found",1);
+
+        if (sdf != loadedSdf) {
+            burtstatus |= 1;
+        }
+        else
+        {
+            burtstatus &= ~(1);
+        }
+		//if(strcmp(sdf,loadedSdf) != 0) burtstatus |= 1;
+		//else burtstatus &= ~(1);
+
+		if(burtstatus == 0) {
+            msgstr_channel.set(" ");
+        }
+		if(burtstatus == 1) {
+            msgstr_channel.set("New SDF File Pending");
+        }
+		if(burtstatus & 2) {
+            msgstr_channel.set("Read Error: Errant line(s) in file");
+        }
+		if(burtstatus & 4) {
+            msgstr_channel.set("Read Error: File Not Found");
+        }
 		if(request != 0) {		// If there is a read file request, then:
-			status = dbPutField(&reload_addr,DBR_LONG,&ropts,1);	// Clear the read request.
+			reload_channel.set(ropts);
 			reqValid = 1;
 			if(reqValid) {
-				rdstatus = readConfig(pref,sdfile,request,sdalarmfile);
+				rdstatus = readConfig(pref,sdfile.c_str(),request,sdalarmfile.c_str());
 				resyncFMArrays(filterMasks,fmMaskChan);
 				if (rdstatus) burtstatus |= rdstatus;
 				else burtstatus &= ~(6);
 				if(burtstatus < 4) {
 					switch (request){
 						case SDF_LOAD_DB_ONLY:
-							strcpy(loadedSdf,sdf); 
-							status = dbPutField(&edbloadedaddr,DBR_STRING,loadedSdf,1);
+                            loadedSdf = sdf;
+                            edbloaded_channel.set(loadedSdf);
 							break;
 						case SDF_RESET:
 							break;
 						case SDF_LOAD_PARTIAL:
-							strcpy(loadedSdf,sdf); 
-							status = dbPutField(&loadedfile_addr,DBR_STRING,loadedSdf,1);
-							status = dbPutField(&edbloadedaddr,DBR_STRING,loadedSdf,1);
+							loadedSdf = sdf;
+                            loadedfile_channel.set(loadedSdf);
+                            edbloaded_channel.set(loadedSdf);
 							break;
 						case SDF_READ_ONLY:
-							strcpy(loadedSdf,sdf); 
-							status = dbPutField(&loadedfile_addr,DBR_STRING,loadedSdf,1);
+							loadedSdf = sdf;
+                            loadedfile_channel.set(loadedSdf);
 							break;
 						default:
 							logFileEntry("Invalid READ Request");
 							reqValid = 0;
 							break;
 					}
-					status = dbPutField(&reloadstat_addr,DBR_LONG,&rdstatus,1);
+                    reload_stat.set(rdstatus);
 					// Get the file CRC for later checking if file changed.
-					sprintf(sdffileloaded, "%s%s%s", sdfDir, loadedSdf,".snap");
-					sdfFileCrc = checkFileCrc(sdffileloaded);
+					sdffileloaded.printf("%s%s%s", sdfDir, loadedSdf.c_str(), ".snap");
+					sdfFileCrc = checkFileCrc(sdffileloaded.c_str());
                     // Get the file mod time
-                    status = checkFileMod( sdffileloaded, &sdfFileMt, 1 );
+                    status = checkFileMod( sdffileloaded.c_str(), &sdfFileMt, 1 );
 					// Calculate and report the number of settings in the BURT file.
 					setChans = chNumP - alarmCnt;
-					status = dbPutField(&filesetcntaddr,DBR_LONG,&setChans,1);
+                    filesetcnt_channel.set(setChans);
 					// Report number of settings in the main table.
 					setChans = chNum - fmNum;
-					status = dbPutField(&fulldbcntaddr,DBR_LONG,&setChans,1);
+                    fulldbcnt_channel.set(setChans);
 					// Sort channels for data reporting via the MEDM table.
-					getEpicsSettings(chNum,NULL);
+					getEpicsSettings();
 					noMon = createSortTableEntries(chNum,0,"",&noInit,NULL);
 					// Calculate and report number of channels NOT being monitored.
-					status = dbPutField(&monchancntaddr,DBR_LONG,&chNotMon,1);
-					status = dbPutField(&alrmchcountaddr,DBR_LONG,&alarmCnt,1);
+					unmonchancnt_channel.set(chNotMon);
+					alrmchcount_channel.set(alarmCnt);
 					// Report number of channels in BURT file that are not in local database.
-					status = dbPutField(&chnotfoundaddr,DBR_LONG,&chNotFound,1);
+					chnotfound_channel.set(chNotFound);
 					// Report number of channels that have not been initialized via a BURT read.
-					status = dbPutField(&chnotinitaddr,DBR_LONG,&chNotInit,1);
+					chnotinit_channel.set(chNotInit);
 					// Write out local monitoring table as snap file.
-					status = writeTable2File(sdfDir,bufile,SDF_WITH_INIT_FLAG,cdTable);
+					writeTable2File(sdfDir,bufile.c_str(),SDF_WITH_INIT_FLAG,cdTable);
 				}
 			}
 		}
-		status = dbPutField(&reloadstat_addr,DBR_LONG,&burtstatus,1);
+        reload_stat.set(burtstatus);
+
 		// sleep(1);
 		// Check for SAVE requests
-		status = dbGetField(&savecmdaddr,DBR_LONG,&sdfSaveReq,&ropts,&nvals,NULL);
+        savecmd_channel.get(sdfSaveReq);
 		if(sdfSaveReq)	// If there is a SAVE file request, then:
 		{
 			// Clear the save file request
-			status = dbPutField(&savecmdaddr,DBR_LONG,&ropts,1);
+            savecmd_channel.set(ropts);
 			// Determine file type
-			status = dbGetField(&savetypeaddr,DBR_STRING,saveTypeString,&ropts,&nvals,NULL);
+            savetype_channel.get(saveTypeString);
 			saveType = 0;
-                        if(strcmp(saveTypeString,"TABLE TO FILE") == 0) saveType = SAVE_TABLE_AS_SDF;
-                        if(strcmp(saveTypeString,"EPICS DB TO FILE") == 0) saveType = SAVE_EPICS_AS_SDF;
+            if(saveTypeString == "TABLE TO FILE") {
+                saveType = SAVE_TABLE_AS_SDF;
+            }
+            if(saveTypeString == "EPICS DB TO FILE") {
+                saveType = SAVE_EPICS_AS_SDF;
+            }
 			// Determine file options
                         saveOpts = 0;
-			status = dbGetField(&saveoptsaddr,DBR_STRING,saveOptsString,&ropts,&nvals,NULL);
-                        if(strcmp(saveOptsString,"TIME NOW") == 0) saveOpts = SAVE_TIME_NOW;
-                        if(strcmp(saveOptsString,"OVERWRITE") == 0) saveOpts = SAVE_OVERWRITE;
-                        if(strcmp(saveOptsString,"SAVE AS") == 0) saveOpts = SAVE_AS;
+			saveopts_channel.get(saveOptsString);
+            if(saveOptsString == "TIME NOW") saveOpts = SAVE_TIME_NOW;
+            if(saveOptsString == "OVERWRITE") saveOpts = SAVE_OVERWRITE;
+            if(saveOptsString == "SAVE AS") saveOpts = SAVE_AS;
 			// Determine if request is valid.
 			if(saveType && saveOpts)
 			{
 				// Get saveas filename
-				status = dbGetField(&saveasaddr,DBR_STRING,saveasfilename,&ropts,&nvals,NULL);
-				if(saveOpts == SAVE_OVERWRITE) 
-					savesdffile(saveType,SAVE_TIME_NOW,sdfDir,modelname,sdfile,saveasfilename,loadedSdf,savefileaddr,savetimeaddr,reloadtimeaddr);
+                saveas_channel.get(saveasfilename);
+				if(saveOpts == SAVE_OVERWRITE) {
+                    savesdffile(saveType, SAVE_TIME_NOW, sdfDir, modelname, sdfile.c_str(), saveasfilename.c_str(), loadedSdf.c_str(),
+                                savefile_channel, savetime_channel, reloadtime_channel);
+                }
 				// Save the file
-				savesdffile(saveType,saveOpts,sdfDir,modelname,sdfile,saveasfilename,loadedSdf,savefileaddr,savetimeaddr,reloadtimeaddr);
+				savesdffile(saveType,saveOpts,sdfDir,modelname,sdfile.c_str(),saveasfilename.c_str(),loadedSdf.c_str(),savefile_channel,savetime_channel,reloadtime_channel);
 				if(saveOpts == SAVE_OVERWRITE)  {
-						sdfFileCrc = checkFileCrc(sdffileloaded);
-                        status = checkFileMod( sdffileloaded, &sdfFileMt, 1 );
+						sdfFileCrc = checkFileCrc(sdffileloaded.c_str());
+                        status = checkFileMod( sdffileloaded.c_str(), &sdfFileMt, 1 );
                 }
 			} else {
 				logFileEntry("Invalid SAVE File Request");
@@ -3585,20 +3588,20 @@ int main(int argc,char *argv[])
 		}
 		// Check present settings vs BURT settings and report diffs.
 		// Check if MON ALL CHANNELS is set
-		status = dbGetField(&monflagaddr,DBR_LONG,&monFlag,&ropts,&nvals,NULL);
-		status = dbGetField(&wcstringaddr,DBR_STRING,wcstring,&ropts,&nvals,NULL);
-		status = dbGetField(&wcstringaddr,DBR_STRING,saveasfilename,&ropts,&nvals,NULL);
+        monflag_channel.get(monFlag);
+		wcstring_channel.get(wcstring);
+		saveasfilename = wcstring;
 		// Call the diff checking function.
 		if(!freezeTable)
-			sperror = spChecker(monFlag,setErrTable,wcVal,wcstring,0,&diffCnt);
+			sperror = spChecker(monFlag,setErrTable,wcVal,wcstring.c_str(),0,&diffCnt);
 		// Report number of diffs found.
-		status = dbPutField(&sperroraddr,DBR_LONG,&diffCnt,1);
+        sperror_channel.set(diffCnt);
 		// Table sorting and presentation
-		status = dbGetField(&tablesortreqaddr,DBR_USHORT,&tsrVal,&ropts,&nvals,NULL);
-		status = dbGetField(&wcreqaddr,DBR_USHORT,&wcVal,&ropts,&nvals,NULL);
+        tablesortreq_channel.set(tsrVal);
+        wcreq_channel.set(wcVal);
 		status = dbGetField(&pagereqaddr,DBR_LONG,&pageNumSet,&ropts,&nvals,NULL);
 		status = dbGetField(&confirmwordaddr,DBR_LONG,&confirmVal,&ropts,&nvals,NULL);
-		status = dbGetField(&selectaddr[3],DBR_LONG,&selectAll,&ropts,&nvals,NULL);
+		select_channel[3].get(selectAll);
 		if(pageNumSet != 0) {
 			pageNum += pageNumSet;
 			if(pageNum < 0) pageNum = 0;
@@ -3609,237 +3612,232 @@ int main(int argc,char *argv[])
 			case SDF_TABLE_DIFFS:
 				// Need to clear selections when moving between tables.
 				if(lastTable !=  SDF_TABLE_DIFFS) {
-					clearTableSelections(sperror,setErrTable, selectCounter);
+					clearTableSelections(setErrTable, selectCounter);
 					resyncFMArrays(filterMasks,fmMaskChan);
 					confirmVal = 0;
 				}
-				pageDisp = reportSetErrors(pref, sperror,setErrTable,pageNum,1);
-				currentTable = setErrTable;
-				currentTableCnt = sperror;
-				status = dbPutField(&sorttableentriesaddr,DBR_LONG,&sperror,1);
-				status = dbGetField(&resetoneaddr,DBR_LONG,&resetNum,&ropts,&nvals,NULL);
+				pageDisp = reportSetErrors(pref, setErrTable,pageNum,1);
+				currentTable = make_range(setErrTable);
+
+                sorttableentries_channel.set(sperror);
+                resetone_channel.get(resetNum);
 				if(selectAll) {
-					setAllTableSelections(sperror,setErrTable, selectCounter,selectAll);
+					setAllTableSelections(make_range(setErrTable), selectCounter,selectAll);
 				}
 				if(resetNum) {
-					decodeChangeSelect(resetNum, pageDisp, sperror, setErrTable,selectCounter, NULL);
+					decodeChangeSelect(resetNum, pageDisp, make_range(setErrTable),selectCounter, NULL);
 				}
 				if(confirmVal) {
-					if(selectCounter[0] && (confirmVal & 2)) status = resetSelectedValues(sperror, setErrTable);
+					if(selectCounter[0] && (confirmVal & 2)) status = resetSelectedValues(setErrTable);
 					if((selectCounter[1] || selectCounter[2]) && (confirmVal & 2)) {
 						// Save present table as timenow.
-						status = dbGetField(&loadedfile_addr,DBR_STRING,backupName,&ropts,&nvals,NULL);
+                        loadedfile_channel.get(backupName);
 						// printf("BACKING UP: %s\n",backupName);
-						savesdffile(SAVE_TABLE_AS_SDF,SAVE_TIME_NOW,sdfDir,modelname,sdfile,saveasfilename,backupName,
-							    savefileaddr,savetimeaddr,reloadtimeaddr);
+						savesdffile(SAVE_TABLE_AS_SDF,SAVE_TIME_NOW,sdfDir,modelname,sdfile.c_str(),saveasfilename.c_str(),
+                                    backupName.c_str(),savefile_channel,savetime_channel,reloadtime_channel);
 						// Overwrite the table with new values
-						status = modifyTable(sperror,setErrTable);
+						status = modifyTable(make_range(setErrTable));
 						// Overwrite file
-						savesdffile(SAVE_TABLE_AS_SDF,SAVE_OVERWRITE,sdfDir,modelname,sdfile,saveasfilename,
-							    backupName,savefileaddr,savetimeaddr,reloadtimeaddr);
-						sdfFileCrc = checkFileCrc(sdffileloaded);
-                        status = checkFileMod( sdffileloaded, &sdfFileMt, 1 );
-						status = writeTable2File(sdfDir,bufile,SDF_WITH_INIT_FLAG,cdTable);
+						savesdffile(SAVE_TABLE_AS_SDF,SAVE_OVERWRITE,sdfDir,modelname,sdfile.c_str(),saveasfilename.c_str(),
+							    backupName.c_str(),savefile_channel,savetime_channel,reloadtime_channel);
+						sdfFileCrc = checkFileCrc(sdffileloaded.c_str());
+                        status = checkFileMod( sdffileloaded.c_str(), &sdfFileMt, 1 );
+						writeTable2File(sdfDir,bufile.c_str(),SDF_WITH_INIT_FLAG,cdTable);
 					}
-					clearTableSelections(sperror,setErrTable, selectCounter);
+					clearTableSelections(setErrTable, selectCounter);
 					resyncFMArrays(filterMasks,fmMaskChan);
 					confirmVal = 0;
 					status = dbPutField(&confirmwordaddr,DBR_LONG,&confirmVal,1);
-					noMon = createSortTableEntries(chNum,wcVal,wcstring,&noInit,NULL);
-					status = dbPutField(&monchancntaddr,DBR_LONG,&chNotMon,1);
+					noMon = createSortTableEntries(chNum,wcVal,wcstring.c_str(),&noInit,NULL);
+                    unmonchancnt_channel.set(chNotMon);
 				}
 				lastTable = SDF_TABLE_DIFFS;
 				break;
 			case SDF_TABLE_NOT_FOUND:
 				// Need to clear selections when moving between tables.
 				if(lastTable != SDF_TABLE_NOT_FOUND) {
-					clearTableSelections(sperror,setErrTable, selectCounter);
+					clearTableSelections(setErrTable, selectCounter);
 					resyncFMArrays(filterMasks,fmMaskChan);
 					confirmVal = 0;
 				}
-				pageDisp = reportSetErrors(pref, chNotFound,unknownChans,pageNum,1);
-				currentTable = unknownChans;
-				currentTableCnt = chNotFound;
-				status = dbPutField(&sorttableentriesaddr,DBR_LONG,&chNotFound,1);
+				pageDisp = reportSetErrors(pref, unknownChans,pageNum,1);
+				currentTable = make_range(unknownChans);
+
+                sorttableentries_channel.set(chNotFound);
 				/*if (resetNum > 200) {
-					decodeChangeSelect(resetNum, pageDisp, chNotFound, unknownChans,selectCounter, NULL);
+					decodeChangeSelect(resetNum, pageDisp, make_range(unknownChans),selectCounter, NULL);
 				}*/
 				lastTable =  SDF_TABLE_NOT_FOUND;
 				break;
 			case SDF_TABLE_NOT_INIT:
 				if(lastTable != SDF_TABLE_NOT_INIT) {
-					clearTableSelections(sperror,setErrTable, selectCounter);
+					clearTableSelections(setErrTable, selectCounter);
 					resyncFMArrays(filterMasks,fmMaskChan);
 					confirmVal = 0;
 				}
 				if (!freezeTable)
-					getEpicsSettings(chNum,NULL);
-				noMon = createSortTableEntries(chNum,wcVal,wcstring,&noInit,NULL);
-				pageDisp = reportSetErrors(pref, noInit, uninitChans,pageNum,1);
-				currentTable = uninitChans;
-				currentTableCnt = noInit;
-				status = dbGetField(&resetoneaddr,DBR_LONG,&resetNum,&ropts,&nvals,NULL);
+					getEpicsSettings();
+				noMon = createSortTableEntries(chNum,wcVal,wcstring.c_str(),&noInit,NULL);
+				pageDisp = reportSetErrors(pref, uninitChans,pageNum,1);
+				currentTable = make_range(uninitChans);
+                resetone_channel.get(resetNum);
 				if(selectAll == 2 || selectAll == 3) {
-					setAllTableSelections(noInit,uninitChans,selectCounter,selectAll);
+					setAllTableSelections(make_range(uninitChans),selectCounter,selectAll);
 					if (selectAll == 3)
-						setAllTableSelections(noInit,uninitChans,selectCounter,2);
+						setAllTableSelections(make_range(uninitChans),selectCounter,2);
 				}
 				if(resetNum > 100) {
-					decodeChangeSelect(resetNum, pageDisp, noInit, uninitChans,selectCounter, changeSelectCB_uninit);
+					decodeChangeSelect(resetNum, pageDisp, make_range(uninitChans),selectCounter, changeSelectCB_uninit);
 				}
 				if(confirmVal) {
-					if(selectCounter[0] && (confirmVal & 2)) status = resetSelectedValues(noInit, uninitChans);
+					if(selectCounter[0] && (confirmVal & 2)) status = resetSelectedValues(uninitChans);
 					if(selectCounter[1] && (confirmVal & 2)) {
 						// Save present table as timenow.
-						status = dbGetField(&loadedfile_addr,DBR_STRING,backupName,&ropts,&nvals,NULL);
+						loadedfile_channel.get(backupName);
 						// printf("BACKING UP: %s\n",backupName);
-						savesdffile(SAVE_TABLE_AS_SDF,SAVE_TIME_NOW,sdfDir,modelname,sdfile,saveasfilename,backupName,
-							    savefileaddr,savetimeaddr,reloadtimeaddr);
+						savesdffile(SAVE_TABLE_AS_SDF,SAVE_TIME_NOW,sdfDir,modelname,sdfile.c_str(),saveasfilename.c_str(),backupName.c_str(),
+							    savefile_channel,savetime_channel,reloadtime_channel);
 						// Overwrite the table with new values
-						status = modifyTable(noInit,uninitChans);
+						status = modifyTable(make_range(uninitChans));
 						// Overwrite file
-						savesdffile(SAVE_TABLE_AS_SDF,SAVE_OVERWRITE,sdfDir,modelname,sdfile,saveasfilename,
-							    backupName,savefileaddr,savetimeaddr,reloadtimeaddr);
-						sdfFileCrc = checkFileCrc(sdffileloaded);
-                        status = checkFileMod( sdffileloaded, &sdfFileMt, 1 );
-						status = writeTable2File(sdfDir,bufile,SDF_WITH_INIT_FLAG,cdTable);
+						savesdffile(SAVE_TABLE_AS_SDF,SAVE_OVERWRITE,sdfDir,modelname,sdfile.c_str(),saveasfilename.c_str(),
+							    backupName.c_str(),savefile_channel,savetime_channel,reloadtime_channel);
+						sdfFileCrc = checkFileCrc(sdffileloaded.c_str());
+                        status = checkFileMod( sdffileloaded.c_str(), &sdfFileMt, 1 );
+						writeTable2File(sdfDir,bufile.c_str(),SDF_WITH_INIT_FLAG,cdTable);
 					}
-					clearTableSelections(chNotInit,uninitChans, selectCounter);
+					clearTableSelections(uninitChans, selectCounter);
 					resyncFMArrays(filterMasks,fmMaskChan);
 					confirmVal = 0;
 					status = dbPutField(&confirmwordaddr,DBR_LONG,&confirmVal,1);
 				}
-				status = dbPutField(&sorttableentriesaddr,DBR_LONG,&noInit,1);
-				status = dbPutField(&chnotinitaddr,DBR_LONG,&chNotInit,1);
+                sorttableentries_channel.set(noInit);
+				chnotinit_channel.set(chNotInit);
 				lastTable = SDF_TABLE_NOT_INIT;
 				break;
 			case SDF_TABLE_NOT_MONITORED:
 				D("In not mon\n");
 				if(lastTable != SDF_TABLE_NOT_MONITORED) {
-					clearTableSelections(noMon,unMonChans, selectCounter);
+					clearTableSelections(unMonChans, selectCounter);
 					resyncFMArrays(filterMasks,fmMaskChan);
 					confirmVal = 0;
 					status = dbPutField(&confirmwordaddr,DBR_LONG,&confirmVal,1);
 				}
 				if (!freezeTable)
-					getEpicsSettings(chNum,NULL); //timeTable);
-				noMon = createSortTableEntries(chNum,wcVal,wcstring,&noInit,NULL);//timeTable);
-				status = dbPutField(&monchancntaddr,DBR_LONG,&chNotMon,1);
-				pageDisp = reportSetErrors(pref, noMon, unMonChans,pageNum,1);
-				currentTable = unMonChans;
-				currentTableCnt = noMon;
-				status = dbGetField(&resetoneaddr,DBR_LONG,&resetNum,&ropts,&nvals,NULL);
+					getEpicsSettings(); //timeTable);
+				noMon = createSortTableEntries(chNum,wcVal,wcstring.c_str(),&noInit,NULL);//timeTable);
+				unmonchancnt_channel.set(chNotMon);
+				pageDisp = reportSetErrors(pref, unMonChans,pageNum,1);
+				currentTable = make_range(unMonChans);
+                resetone_channel.get(resetNum);
 				if(selectAll) {
-					setAllTableSelections(noMon,unMonChans,selectCounter,selectAll);
+					setAllTableSelections(make_range(unMonChans),selectCounter,selectAll);
 				}
 				if(resetNum) {
-					decodeChangeSelect(resetNum, pageDisp, noMon, unMonChans,selectCounter, NULL);
+					decodeChangeSelect(resetNum, pageDisp, make_range(unMonChans),selectCounter, NULL);
 				}
 				if(confirmVal) {
-					if(selectCounter[0] && (confirmVal & 2)) status = resetSelectedValues(noMon, unMonChans);
+					if(selectCounter[0] && (confirmVal & 2)) status = resetSelectedValues(unMonChans);
 					if((selectCounter[1] || selectCounter[2]) && (confirmVal & 2)) {
 						// Save present table as timenow.
-						status = dbGetField(&loadedfile_addr,DBR_STRING,backupName,&ropts,&nvals,NULL);
+                        loadedfile_channel.get(backupName);
 						// printf("BACKING UP: %s\n",backupName);
-						savesdffile(SAVE_TABLE_AS_SDF,SAVE_TIME_NOW,sdfDir,modelname,sdfile,saveasfilename,backupName,
-							    savefileaddr,savetimeaddr,reloadtimeaddr);
+						savesdffile(SAVE_TABLE_AS_SDF,SAVE_TIME_NOW,sdfDir,modelname,sdfile.c_str(),saveasfilename.c_str(),backupName.c_str(),
+							    savefile_channel,savetime_channel,reloadtime_channel);
 						// Overwrite the table with new values
-						status = modifyTable(noMon,unMonChans);
+						status = modifyTable(make_range(unMonChans));
 						// Overwrite file
-						savesdffile(SAVE_TABLE_AS_SDF,SAVE_OVERWRITE,sdfDir,modelname,sdfile,saveasfilename,
-							    backupName,savefileaddr,savetimeaddr,reloadtimeaddr);
-						sdfFileCrc = checkFileCrc(sdffileloaded);
-                        status = checkFileMod( sdffileloaded, &sdfFileMt, 1 );
-						status = writeTable2File(sdfDir,bufile,SDF_WITH_INIT_FLAG,cdTable);
+						savesdffile(SAVE_TABLE_AS_SDF,SAVE_OVERWRITE,sdfDir,modelname,sdfile.c_str(),saveasfilename.c_str(),
+							    backupName.c_str(),savefile_channel,savetime_channel,reloadtime_channel);
+						sdfFileCrc = checkFileCrc(sdffileloaded.c_str());
+                        status = checkFileMod( sdffileloaded.c_str(), &sdfFileMt, 1 );
+						writeTable2File(sdfDir,bufile.c_str(),SDF_WITH_INIT_FLAG,cdTable);
 					}
 					// noMon = createSortTableEntries(chNum,wcVal,wcstring);
-					clearTableSelections(noMon,unMonChans, selectCounter);
+					clearTableSelections(unMonChans, selectCounter);
 					resyncFMArrays(filterMasks,fmMaskChan);
 					confirmVal = 0;
 					status = dbPutField(&confirmwordaddr,DBR_LONG,&confirmVal,1);
 					// Calculate and report number of channels NOT being monitored.
 				}
-				status = dbPutField(&sorttableentriesaddr,DBR_LONG,&noMon,1);
+                sorttableentries_channel.set(noMon);
 				lastTable = SDF_TABLE_NOT_MONITORED;
 				break;
 			case SDF_TABLE_FULL:
 				if(lastTable != SDF_TABLE_FULL) {
-					clearTableSelections(cdSort,cdTableList, selectCounter);
+					clearTableSelections(cdTableList, selectCounter);
 					resyncFMArrays(filterMasks,fmMaskChan);
 					confirmVal = 0;
 				}
 				if (!freezeTable)
-					cdSort = spChecker(monFlag,cdTableList,wcVal,wcstring,1,&status);
-				pageDisp = reportSetErrors(pref, cdSort, cdTableList,pageNum,1);
-				currentTable = cdTableList;
-				currentTableCnt = cdSort;
-				status = dbGetField(&resetoneaddr,DBR_LONG,&resetNum,&ropts,&nvals,NULL);
+					cdSort = spChecker(monFlag,cdTableList,wcVal,wcstring.c_str(),1,&status);
+				pageDisp = reportSetErrors(pref, cdTableList,pageNum,1);
+				currentTable = make_range(cdTableList);
+                resetone_channel.get(resetNum);
 				if(selectAll == 3) {
-					setAllTableSelections(cdSort,cdTableList,selectCounter,selectAll);
+					setAllTableSelections(make_range(cdTableList),selectCounter,selectAll);
 				}
 				if(resetNum) {
-					decodeChangeSelect(resetNum, pageDisp, cdSort, cdTableList,selectCounter, NULL);
+					decodeChangeSelect(resetNum, pageDisp, make_range(cdTableList),selectCounter, NULL);
 				}
 				if(confirmVal) {
-					if(selectCounter[0] && (confirmVal & 2)) status = resetSelectedValues(cdSort, cdTableList);
+					if(selectCounter[0] && (confirmVal & 2)) status = resetSelectedValues(cdTableList);
 					if((selectCounter[1] || selectCounter[2]) && (confirmVal & 2)) {
 						// Save present table as timenow.
-						status = dbGetField(&loadedfile_addr,DBR_STRING,backupName,&ropts,&nvals,NULL);
+                        loadedfile_channel.get(backupName);
 						// printf("BACKING UP: %s\n",backupName);
-						savesdffile(SAVE_TABLE_AS_SDF,SAVE_TIME_NOW,sdfDir,modelname,sdfile,saveasfilename,
-							    backupName,savefileaddr,savetimeaddr,reloadtimeaddr);
+						savesdffile(SAVE_TABLE_AS_SDF,SAVE_TIME_NOW,sdfDir,modelname,sdfile.c_str(),saveasfilename.c_str(),
+							    backupName.c_str(),savefile_channel,savetime_channel,reloadtime_channel);
 						// Overwrite the table with new values
-						status = modifyTable(cdSort,cdTableList);
+						status = modifyTable(make_range(cdTableList));
 						// Overwrite file
-						savesdffile(SAVE_TABLE_AS_SDF,SAVE_OVERWRITE,sdfDir,modelname,sdfile,saveasfilename,
-							    backupName,savefileaddr,savetimeaddr,reloadtimeaddr);
-						sdfFileCrc = checkFileCrc(sdffileloaded);
-                        status = checkFileMod( sdffileloaded, &sdfFileMt, 1 );
-						status = writeTable2File(sdfDir,bufile,SDF_WITH_INIT_FLAG,cdTable);
+						savesdffile(SAVE_TABLE_AS_SDF,SAVE_OVERWRITE,sdfDir,modelname,sdfile.c_str(),saveasfilename.c_str(),
+							    backupName.c_str(),savefile_channel,savetime_channel,reloadtime_channel);
+						sdfFileCrc = checkFileCrc(sdffileloaded.c_str());
+                        status = checkFileMod( sdffileloaded.c_str(), &sdfFileMt, 1 );
+						writeTable2File(sdfDir,bufile.c_str(),SDF_WITH_INIT_FLAG,cdTable);
 					}
-					clearTableSelections(cdSort,cdTableList, selectCounter);
+					clearTableSelections(cdTableList, selectCounter);
 					resyncFMArrays(filterMasks,fmMaskChan);
 					confirmVal = 0;
 					status = dbPutField(&confirmwordaddr,DBR_LONG,&confirmVal,1);
-					noMon = createSortTableEntries(chNum,wcVal,wcstring,&noInit,NULL);
+					noMon = createSortTableEntries(chNum,wcVal,wcstring.c_str(),&noInit,NULL);
 					// Calculate and report number of channels NOT being monitored.
-					status = dbPutField(&monchancntaddr,DBR_LONG,&chNotMon,1);
+					unmonchancnt_channel.set(chNotMon);
 				}
-				status = dbPutField(&sorttableentriesaddr,DBR_LONG,&cdSort,1);
+                sorttableentries_channel.set(cdSort);
 				lastTable = SDF_TABLE_FULL;
 				break;
 #ifdef CA_SDF
 			case SDF_TABLE_DISCONNECTED:
 				if(lastTable != SDF_TABLE_DISCONNECTED) {
-					clearTableSelections(cdSort,cdTableList, selectCounter);
+					clearTableSelections(cdTableList, selectCounter);
 					resyncFMArrays(filterMasks,fmMaskChan);
 					confirmVal = 0;
 				}
-				noMon = createSortTableEntries(chNum,wcVal,wcstring,&noInit,NULL);
-				pageDisp =  reportSetErrors(pref, chDisconnected, disconnectChans, pageNum,0);
+				noMon = createSortTableEntries(chNum,wcVal,wcstring.c_str(),&noInit,NULL);
+				pageDisp =  reportSetErrors(pref, disconnectChans, pageNum,0);
 				chDisconnectedCount = chDisconnected;
-				currentTable = disconnectChans;
-				currentTableCnt = chDisconnected;
-				status = dbPutField(&sorttableentriesaddr,DBR_LONG,&chDisconnected, 1);
+				currentTable = make_range(disconnectChans);
+                sorttableentries_channel.set(chDisconnected);
 				if (resetNum > 200) {
-					decodeChangeSelect(resetNum, pageDisp, chDisconnected, disconnectChans, selectCounter, NULL);
+					decodeChangeSelect(resetNum, pageDisp, make_range(disconnectChans), selectCounter, NULL);
 				}
 				break;
 #endif
 			default:
-				pageDisp = reportSetErrors(pref, sperror,setErrTable,pageNum,1);
-				status = dbPutField(&sorttableentriesaddr,DBR_LONG,&sperror,1);
-				currentTable = setErrTable;
-				currentTableCnt = sperror;
+				pageDisp = reportSetErrors(pref, setErrTable,pageNum,1);
+				sorttableentries_channel.set(sperror);
+				currentTable = make_range(setErrTable);
 				break;
 		}
 		if (selectAll) {
 			selectAll = 0;
-			status = dbPutField(&selectaddr[3],DBR_LONG,&selectAll,1);
+            select_channel[3].set(selectAll);
 		}
 		if (resetNum) {
 			resetNum = 0;
-			status = dbPutField(&resetoneaddr,DBR_LONG,&resetNum,1);
+            resetone_channel.set(resetNum);
 		}
 		if(pageDisp != pageNum) {
 			pageNum = pageDisp;
@@ -3847,11 +3845,11 @@ int main(int argc,char *argv[])
 		freezeTable = 0;
 		for(ii=0;ii<3;ii++) {
 			freezeTable += selectCounter[ii];
-			status = dbPutField(&selectaddr[ii],DBR_LONG,&selectCounter[ii],1);
+            select_channel[ii].set(selectCounter[ii]);
 		}
-		status = dbPutField(&(pagelockaddr[0]),DBR_LONG,&freezeTable,1);
+        pagelock_channel.set(freezeTable);
 
-		processFMChanCommands(filterMasks, fmMaskChan,fmMaskChanCtrl,selectCounter, currentTableCnt, currentTable);
+		processFMChanCommands(filterMasks, fmMaskChan,fmMaskChanCtrl,selectCounter, currentTable);
 
 		// Check file CRCs every 5 seconds.
 		// DAQ and COEFF file checking was moved from skeleton.st to here RCG V2.9.
@@ -3863,7 +3861,7 @@ int main(int argc,char *argv[])
 			status = checkFileCrc(daqFile);
 			if(status != daqFileCrc) {
 				daqFileCrc = status;
-				status = dbPutField(&daqmsgaddr,DBR_STRING,modfilemsg,1);
+                daqmsg_channel.set(modfilemsg);
 				logFileEntry("Detected Change to DAQ Config file.");
 			}
             }
@@ -3875,9 +3873,9 @@ int main(int argc,char *argv[])
 			coeffFileCrc = checkFileCrc(coeffFile);
 			fotonFileCrc = checkFileCrc(fotonFile);
 			if(fotonFileCrc != coeffFileCrc) {
-				status = dbPutField(&coeffmsgaddr,DBR_STRING,modfilemsg,1);
+                coeffmsg_channel.set(modfilemsg);
 			} else {
-				status = dbPutField(&coeffmsgaddr,DBR_STRING,"",1);
+                coeffmsg_channel.set("");
 			}
 			if(fotonFileCrc != prevFotonFileCrc || prevCoeffFileCrc != coeffFileCrc) {
 				sprintf(myDiffCmd,"%s %s %s %s %s","diff",fotonFile,coeffFile," > ",fotonDiffFile);
@@ -3887,19 +3885,18 @@ int main(int argc,char *argv[])
 			}
             }
             // Check SDF file modified
-            fm_flag = checkFileMod( sdffileloaded, &sdfFileMt, 0 );
+            fm_flag = checkFileMod( sdffileloaded.c_str(), &sdfFileMt, 0 );
             if ( fm_flag )
             {
-			status = checkFileCrc(sdffileloaded);
+			status = checkFileCrc(sdffileloaded.c_str());
 			if(status == -1) {
 				sdfFileCrc = status;
 				logFileEntry("SDF file not found.");
-				dbPutField(&reloadtimeaddr,DBR_STRING,"File Not Found",1);
-			} 
+			}
 			if(status != sdfFileCrc) {
 				sdfFileCrc = status;
 				logFileEntry("Detected Change to SDF file.");
-				dbPutField(&reloadtimeaddr,DBR_STRING,modfilemsg,1);
+				reloadtime_channel.set(modfilemsg);
 			}
             }
 		}
@@ -3908,6 +3905,6 @@ int main(int argc,char *argv[])
 	sleep(0xfffffff);
     } else
     	iocsh(NULL);
-    CLEANUP;
+    CLEANUP();
     return(0);
 }
diff --git a/src/epics/seq/sdf_monitor.c b/src/epics/seq/sdf_monitor.c
index 13d3cfe38c2635b9a50ee99c6d883b86ea95749f..cab7c70b588da56043a098d4ebe373d13825ea39 100644
--- a/src/epics/seq/sdf_monitor.c
+++ b/src/epics/seq/sdf_monitor.c
@@ -44,6 +44,7 @@ of this distribution.
 #include "cadef.h"
 #include "fb.h"
 #include "../../drv/gpstime/gpstime.h"
+#include "util/user/check_file_crc.h"
 
 #define GSDF_MAX_CHANS	30000
 // Gloabl variables		****************************************************************************************
@@ -57,7 +58,6 @@ char statelogfilename[128];
 unsigned char naughtyList[GSDF_MAX_CHANS][64];
 
 // Function prototypes		****************************************************************************************
-int checkFileCrc(char *);
 void getSdfTime(char *);
 void logFileEntry(char *);
 
@@ -444,29 +444,6 @@ void gsdfInitialize(char *shmem_fname)
 }
 
 
-
-/// Common routine to check file CRC.
-///	@param[in] *fName	Name of file to check.
-///	@return File CRC or -1 if file not found.
-int checkFileCrc(char *fName)
-{
-char buffer[256];
-FILE *pipePtr;
-struct stat statBuf;
-long chkSum = -99999;
-      	strcpy(buffer, "cksum "); 
-      	strcat(buffer, fName); 
-      	if (!stat(fName, &statBuf) ) { 
-         	if ((pipePtr = popen(buffer, "r")) != NULL) {
-            	fgets(buffer, 256, pipePtr);
-            	pclose(pipePtr); 
-            	sscanf(buffer, "%ld", &chkSum);
-         	} 
-		return(chkSum);
-    	}    
-	return(-1);
-}
-
 /// Routine for reading GPS time from model EPICS record.
 ///	@param[out] timestring 	Pointer to char string in which GPS time is to be written.
 void getSdfTime(char *timestring)
diff --git a/src/epics/seq/simple_range.hh b/src/epics/seq/simple_range.hh
new file mode 100644
index 0000000000000000000000000000000000000000..36cbb0aefb8bc0e0af4e61b4d3db54a1d575fec6
--- /dev/null
+++ b/src/epics/seq/simple_range.hh
@@ -0,0 +1,78 @@
+//
+// Created by jonathan.hanks on 11/17/20.
+//
+
+#ifndef DAQD_TRUNK_SIMPLE_RANGE_HH
+#define DAQD_TRUNK_SIMPLE_RANGE_HH
+
+#include <type_traits>
+#include <utility>
+
+namespace embedded
+{
+    /*!
+     * @brief a very simple range class, just tracks two iterators in one unit, [start, end)
+     * @tparam It the iterator type, which must be noexcept on copy and move
+     */
+    template <typename It>
+    class range
+    {
+        static_assert(std::is_nothrow_copy_constructible<It>::value, "It must be no throw copy constructable");
+        static_assert(std::is_nothrow_copy_assignable<It>::value, "It must be no throw copyable");
+        static_assert(std::is_nothrow_move_constructible<It>::value, "It must be no throw move constructable");
+        static_assert(std::is_nothrow_move_assignable<It>::value, "It must be no throw movable");
+    public:
+        using iterator = It;
+
+        range() noexcept: start_{}, end_{} {}
+        range(const range&) noexcept = default;
+        range(range&&) noexcept = default;
+        range(It it1, It it2) noexcept: start_{std::move(it1)}, end_{std::move(it2)} {}
+        range& operator=(const range&) noexcept = default;
+        range& operator=(range&&) noexcept = default;
+
+        iterator
+        begin() noexcept
+        {
+            return start_;
+        }
+
+        iterator
+        end() noexcept
+        {
+            return end_;
+        }
+
+        const iterator
+        begin() const noexcept
+        {
+            return start_;
+        }
+
+        const iterator
+        end() const noexcept
+        {
+            return end_;
+        }
+
+    private:
+        It start_;
+        It end_;
+    };
+
+    /*!
+     * @brief Given two iterators of type It construct a range
+     * @tparam It
+     * @param start
+     * @param end
+     * @return the resulting range
+     */
+    template <typename It>
+    range<It>
+    make_range(It start, It end)
+    {
+        return range<It>(std::move(start), std::move(end));
+    }
+}
+
+#endif //DAQD_TRUNK_SIMPLE_RANGE_HH
diff --git a/src/epics/seq/test/test_fixed_size_string.cc b/src/epics/seq/test/test_fixed_size_string.cc
new file mode 100644
index 0000000000000000000000000000000000000000..4d808ef4ad1be346f712a248b72131433f0062e4
--- /dev/null
+++ b/src/epics/seq/test/test_fixed_size_string.cc
@@ -0,0 +1,378 @@
+//
+// Created by jonathan.hanks on 8/16/18.
+//
+#include "catch.hpp"
+
+#include "fixed_size_string.hh"
+
+#include <cstring>
+#include <type_traits>
+#include <vector>
+
+static_assert(
+    std::is_trivially_copyable< embedded::fixed_string< 64 > >::value,
+    "Must be trivial" );
+static_assert( std::is_standard_layout< embedded::fixed_string< 64 > >::value,
+               "Must be standard layout" );
+static_assert(
+    std::is_nothrow_constructible< embedded::fixed_string< 64 > >::value,
+    "Must be nothrow constructable" );
+static_assert(
+    std::is_nothrow_copy_constructible< embedded::fixed_string< 64 > >::value,
+    "Must be nothrow copy constructable" );
+static_assert(
+    std::is_nothrow_copy_assignable< embedded::fixed_string< 64 > >::value,
+    "Must be nothrow copy assignable" );
+static_assert( std::is_nothrow_swappable< embedded::fixed_string< 64 > >::value,
+               "Must be nothrow swappable" );
+
+TEST_CASE( "You can create a fixed_size_string" )
+{
+
+    SECTION( "Create an empty string" )
+    {
+        embedded::fixed_string< 64 > s;
+        REQUIRE( s.capacity( ) == 64 );
+        REQUIRE( s.remaining( ) == 63 );
+        REQUIRE( sizeof( s ) >= 64 );
+        REQUIRE( s.size( ) == 0 );
+        REQUIRE( std::strcmp( s.c_str( ), "" ) == 0 );
+        REQUIRE( s.c_str( ) == s.data( ) );
+    }
+    SECTION( "Create a string from a literal" )
+    {
+        std::vector< const char* > tests;
+        tests.push_back( "This is a test" );
+        tests.push_back( (char*)0 );
+        tests.push_back( "" );
+        tests.push_back( " " );
+        tests.push_back(
+            "123456789012345678901234567890123456789012345678901234567890123" );
+        tests.push_back( "12345678901234567890123456789012345678901234567890123"
+                         "45678901234" );
+        tests.push_back( "12345678901234567890123456789012345678901234567890123"
+                         "456789012345" );
+
+        for ( int i = 0; i < tests.size( ); ++i )
+        {
+            embedded::fixed_string< 64 > s( tests[ i ] );
+            REQUIRE( s.capacity( ) == 64 );
+            REQUIRE( s.c_str( ) == s.data( ) );
+            if ( tests[ i ] )
+            {
+                if ( strlen( tests[ i ] ) >= s.capacity( ) )
+                {
+                    REQUIRE( strncmp( s.c_str( ),
+                                      tests[ i ],
+                                      s.capacity( ) - 1 ) == 0 );
+                    REQUIRE( strlen( s.c_str( ) ) == s.capacity( ) - 1 );
+                    REQUIRE( s.size( ) == s.capacity( ) - 1 );
+                }
+                else
+                {
+                    REQUIRE( strcmp( s.c_str( ), tests[ i ] ) == 0 );
+                    REQUIRE( strlen( tests[ i ] ) == s.size( ) );
+                }
+            }
+            else
+            {
+                REQUIRE( strcmp( s.c_str( ), "" ) == 0 );
+                REQUIRE( s.size( ) == 0 );
+            }
+        }
+    }
+    SECTION( "Create via copy constructor" )
+    {
+        embedded::fixed_string< 64 > s0( "Hello World!" );
+        embedded::fixed_string< 64 > s1( s0 );
+        REQUIRE( s0.size( ) == s1.size( ) );
+        REQUIRE( s0.capacity( ) == s1.capacity( ) );
+        REQUIRE( strcmp( s0.c_str( ), s1.c_str( ) ) == 0 );
+        REQUIRE( strcmp( s0.c_str( ), "Hello World!" ) == 0 );
+    }
+    SECTION( "Create via copy constructor from different size "
+             "embedded::fixed_string using copy constructor" )
+    {
+        embedded::fixed_string< 32 > s0( "Hello World!" );
+        embedded::fixed_string< 64 > s1( s0 );
+        REQUIRE( s0.size( ) == s1.size( ) );
+        REQUIRE( s0.capacity( ) == 32 );
+        REQUIRE( s1.capacity( ) == 64 );
+        REQUIRE( strcmp( s0.c_str( ), s1.c_str( ) ) == 0 );
+        REQUIRE( strcmp( s0.c_str( ), "Hello World!" ) == 0 );
+    }
+    SECTION( "Create via copy constructor to a smaller size "
+             "embedded::fixed_string using copy constructor" )
+    {
+        embedded::fixed_string< 32 > s0( "Hello World!" );
+        embedded::fixed_string< 6 >  s1( s0 );
+        REQUIRE( s0.size( ) == 12 );
+        REQUIRE( s1.size( ) == 5 );
+        REQUIRE( s0.capacity( ) == 32 );
+        REQUIRE( s1.capacity( ) == 6 );
+        REQUIRE( strcmp( s0.c_str( ), s1.c_str( ) ) != 0 );
+        REQUIRE( strcmp( s1.c_str( ), "Hello" ) == 0 );
+    }
+    SECTION( "Create via copy constructor from different size "
+             "embedded::fixed_string using c_str()" )
+    {
+        embedded::fixed_string< 32 > s0( "Hello World!" );
+        embedded::fixed_string< 64 > s1( s0.c_str( ) );
+        REQUIRE( s0.size( ) == s1.size( ) );
+        REQUIRE( s0.capacity( ) == 32 );
+        REQUIRE( s1.capacity( ) == 64 );
+        REQUIRE( strcmp( s0.c_str( ), s1.c_str( ) ) == 0 );
+        REQUIRE( strcmp( s0.c_str( ), "Hello World!" ) == 0 );
+    }
+    SECTION( "Copy operators should work" )
+    {
+        embedded::fixed_string< 64 > s;
+        REQUIRE( s.size( ) == 0 );
+        embedded::fixed_string< 64 > other( "Hello World!" );
+        s = other;
+        REQUIRE( s.size( ) == other.size( ) );
+        REQUIRE( strcmp( s.c_str( ), other.c_str( ) ) == 0 );
+
+        const char* goodbye = "Goodbye World!";
+        s = goodbye;
+        REQUIRE( s.size( ) == strlen( goodbye ) );
+        REQUIRE( strcmp( s.c_str( ), goodbye ) == 0 );
+
+        std::vector< const char* > tests;
+        tests.push_back( "This is a test" );
+        tests.push_back( (char*)0 );
+        tests.push_back( "" );
+        tests.push_back( " " );
+        tests.push_back(
+            "123456789012345678901234567890123456789012345678901234567890123" );
+        tests.push_back( "12345678901234567890123456789012345678901234567890123"
+                         "45678901234" );
+        tests.push_back( "12345678901234567890123456789012345678901234567890123"
+                         "456789012345" );
+
+        for ( int i = 0; i < tests.size( ); ++i )
+        {
+            s = tests[ i ];
+            if ( tests[ i ] )
+            {
+                if ( strlen( tests[ i ] ) >= s.capacity( ) )
+                {
+                    REQUIRE( strncmp( s.c_str( ),
+                                      tests[ i ],
+                                      s.capacity( ) - 1 ) == 0 );
+                    REQUIRE( strlen( s.c_str( ) ) == s.capacity( ) - 1 );
+                    REQUIRE( s.size( ) == s.capacity( ) - 1 );
+                }
+                else
+                {
+                    REQUIRE( strcmp( s.c_str( ), tests[ i ] ) == 0 );
+                    REQUIRE( strlen( tests[ i ] ) == s.size( ) );
+                }
+            }
+            else
+            {
+                REQUIRE( strcmp( s.c_str( ), "" ) == 0 );
+                REQUIRE( s.size( ) == 0 );
+            }
+        }
+    }
+}
+
+TEST_CASE( "Copy operators work even when the sizes are different, it just may "
+           "truncate" )
+{
+    embedded::fixed_string< 64 > s0( "123456" );
+    embedded::fixed_string< 32 > s1;
+    embedded::fixed_string< 6 >  s2;
+    embedded::fixed_string< 64 > s3( "123456" );
+
+    s1 = s0;
+    REQUIRE( s1 == "123456" );
+    s2 = s0;
+    REQUIRE( s2 == "12345" );
+    s3 = s2;
+    REQUIRE( s3 == "12345" );
+}
+
+TEST_CASE( "You can append to a fixed size string" )
+{
+    embedded::fixed_string< 64 > s;
+    embedded::fixed_string< 6 >  single( "0" );
+    embedded::fixed_string< 6 >  five( "00000" );
+    for ( int i = 1; i <= 100; ++i )
+    {
+        s += "0";
+        std::size_t expected_zeros =
+            ( i < s.capacity( ) ? i : s.capacity( ) - 1 );
+        REQUIRE( s.size( ) == expected_zeros );
+        std::size_t j = 0;
+        for ( ; j < expected_zeros && s.c_str( )[ j ] == '0'; ++j )
+        {
+        }
+        REQUIRE( j == expected_zeros );
+    }
+    s = "";
+    for ( int i = 1; i <= 100; ++i )
+    {
+        s += "00000";
+        std::size_t expected_zeros =
+            ( i * 5 < s.capacity( ) ? i * 5 : s.capacity( ) - 1 );
+        REQUIRE( s.size( ) == expected_zeros );
+        std::size_t j = 0;
+        for ( ; j < expected_zeros && s.c_str( )[ j ] == '0'; ++j )
+        {
+        }
+        REQUIRE( j == expected_zeros );
+    }
+    s = "";
+    for ( int i = 1; i <= 100; ++i )
+    {
+        s += single;
+        std::size_t expected_zeros =
+            ( i < s.capacity( ) ? i : s.capacity( ) - 1 );
+        REQUIRE( s.size( ) == expected_zeros );
+        std::size_t j = 0;
+        for ( ; j < expected_zeros && s.c_str( )[ j ] == '0'; ++j )
+        {
+        }
+        REQUIRE( j == expected_zeros );
+    }
+    s = "";
+    for ( int i = 1; i <= 100; ++i )
+    {
+        s += five;
+        std::size_t expected_zeros =
+            ( i * 5 < s.capacity( ) ? i * 5 : s.capacity( ) - 1 );
+        REQUIRE( s.size( ) == expected_zeros );
+        std::size_t j = 0;
+        for ( ; j < expected_zeros && s.c_str( )[ j ] == '0'; ++j )
+        {
+        }
+        REQUIRE( j == expected_zeros );
+    }
+    s = "abab";
+    s += s;
+    REQUIRE( s.size( ) == 8 );
+    REQUIRE( strcmp( s.c_str( ), "abababab" ) == 0 );
+    REQUIRE( s.remaining( ) == 63 - 8 );
+}
+
+TEST_CASE( "You can do printf style formatted printing" )
+{
+    embedded::fixed_string< 64 > s;
+    s.printf( "%d", 123456 );
+    REQUIRE( strcmp( s.c_str( ), "123456" ) == 0 );
+    s.printf( "%s", "abcdefg" );
+    REQUIRE( strcmp( s.c_str( ), "abcdefg" ) == 0 );
+    s.printf(
+        "%s",
+        "12345678901234567890123456789012345678901234567890123456789012345" );
+    REQUIRE( s.size( ) == 63 );
+    REQUIRE( strcmp( s.c_str( ),
+                     "123456789012345678901234567890123456789012345678901234567"
+                     "890123" ) == 0 );
+    s.printf(
+        "%d %s",
+        -1,
+        "12345678901234567890123456789012345678901234567890123456789012345" );
+    REQUIRE( s.size( ) == 63 );
+    REQUIRE(
+        strcmp(
+            s.c_str( ),
+            "-1 "
+            "123456789012345678901234567890123456789012345678901234567890" ) ==
+        0 );
+}
+
+TEST_CASE( "You can assign and append via char arrays" )
+{
+    embedded::fixed_string< 64 > s0( "123456" );
+    char                         blob[ 1 ][ 8 ] = { "abc" };
+
+    s0 += blob[ 0 ];
+    REQUIRE( s0 == "123456abc" );
+}
+
+TEST_CASE( "clear will reset the fixed_string to be empty" )
+{
+    embedded::fixed_string< 64 > s0( "123456" );
+    REQUIRE( s0.size( ) > 0 );
+    s0.clear( );
+    REQUIRE( s0.size( ) == 0 );
+    REQUIRE( s0 == "" );
+}
+
+TEST_CASE(
+    "printf with a null format string is equal to a format string of \"\"" )
+{
+    embedded::fixed_string< 64 > s0( "123456" );
+    REQUIRE( s0.printf( nullptr ) == 0 );
+    REQUIRE( s0 == "" );
+}
+
+TEST_CASE(
+    "You can compare with a fixed_string using operator== and operator!=" )
+{
+    embedded::fixed_string< 64 > s0( "123456" ), s1( "123456789" );
+
+    REQUIRE( s0 == s0 );
+    REQUIRE( s0 != s1 );
+}
+
+TEST_CASE( "You can compare with a fixed_string using operator== and "
+           "operator!= even when capacities are different" )
+{
+    embedded::fixed_string< 64 > s0( "1234" ), s1( "123456789" );
+    embedded::fixed_string< 6 >  s2( "1234" );
+
+    REQUIRE( s0 == s2 );
+    REQUIRE( s1 != s2 );
+}
+
+TEST_CASE( "You can compare with a char* using operator== and operator!=" )
+{
+    embedded::fixed_string< 64 > s0( "123456" );
+
+    REQUIRE( s0 == "123456" );
+    REQUIRE( s0 != "1234567890" );
+}
+
+TEST_CASE( "It is safe to compare with a null char*, it matches an empty "
+           "fixed_string" )
+{
+    embedded::fixed_string< 64 > s0( "123456" );
+    embedded::fixed_string< 64 > s1;
+    REQUIRE( s0.size( ) > 0 );
+    REQUIRE( s1.size( ) == 0 );
+    REQUIRE( s0 != nullptr );
+    REQUIRE( s1 == "" );
+    REQUIRE( s1 == nullptr );
+}
+
+TEST_CASE( "You can remove characters from the end" )
+{
+    embedded::fixed_string< 64 > s0( "1234567890" );
+
+    REQUIRE( s0.size( ) == 10 );
+    s0.pop_back_n( 4 );
+    REQUIRE( s0.size( ) == 6 );
+    REQUIRE( s0 == "123456" );
+    s0.pop_back_n( 1 );
+    REQUIRE( s0.size( ) == 5 );
+    s0.pop_back_n( 10 );
+    REQUIRE( s0.size( ) == 0 );
+}
+
+TEST_CASE( "For direct manipulation you can get a buffer which cleans up when "
+           "it is done" )
+{
+    embedded::fixed_string< 10 > s0( "abc" );
+
+    {
+        auto buf = s0.get_buffer( );
+        std::fill( buf.data( ), buf.data( ) + buf.capacity( ), 1 );
+        REQUIRE( buf.data( )[ buf.capacity( ) - 1 ] == 1 );
+        REQUIRE( s0.data( )[ s0.capacity( ) - 1 ] == 1 );
+    }
+    REQUIRE( s0.data( )[ s0.capacity( ) - 1 ] == '\0' );
+}
\ No newline at end of file
diff --git a/src/epics/seq/test/test_fixed_size_vector.cc b/src/epics/seq/test/test_fixed_size_vector.cc
new file mode 100644
index 0000000000000000000000000000000000000000..602dd286a92e73c2d5bf9caec2478218905d5df4
--- /dev/null
+++ b/src/epics/seq/test/test_fixed_size_vector.cc
@@ -0,0 +1,223 @@
+//
+// Created by jonathan.hanks on 11/13/20.
+//
+
+#include "catch.hpp"
+
+#include "fixed_size_vector.hh"
+
+#include <algorithm>
+
+TEST_CASE( "You can create a fixed size vector" )
+{
+    embedded::fixed_size_vector< int, 20 > v0;
+
+    REQUIRE( v0.size( ) == 0 );
+    REQUIRE( v0.empty( ) );
+    REQUIRE( v0.capacity( ) == 20 );
+    REQUIRE( v0.begin( ) == v0.end( ) );
+
+    const embedded::fixed_size_vector< int, 20 > v1;
+    REQUIRE( v1.begin( ) == v1.end( ) );
+    REQUIRE( v1.cbegin( ) == v1.cend( ) );
+    REQUIRE( v1.begin( ) == v1.cbegin( ) );
+    REQUIRE( v1.end( ) == v1.cend( ) );
+
+    static_assert(
+        std::is_same<
+            int*,
+            typename embedded::fixed_size_vector< int, 20 >::iterator >::value,
+        "The iterator is a pointer" );
+    static_assert(
+        std::is_same< const int*,
+                      typename embedded::fixed_size_vector< int, 20 >::
+                          const_iterator >::value,
+        "The iterator is pointer to const" );
+}
+
+TEST_CASE( "You can add elements to the back of a fixed size vector" )
+{
+    embedded::fixed_size_vector< int, 20 > v0;
+    REQUIRE( v0.push_back( 5 ) );
+    REQUIRE( v0.size( ) == 1 );
+    REQUIRE( v0.capacity( ) == 20 );
+    REQUIRE( v0.push_back( 6 ) );
+    REQUIRE( v0.size( ) == 2 );
+    REQUIRE( v0.capacity( ) == 20 );
+    REQUIRE( v0.push_back( 7 ) );
+    REQUIRE( v0.size( ) == 3 );
+    REQUIRE( v0.capacity( ) == 20 );
+    REQUIRE( v0.end( ) == v0.begin( ) + v0.size( ) );
+    REQUIRE( *v0.begin( ) == 5 );
+    REQUIRE( *( v0.begin( ) + 1 ) == 6 );
+    REQUIRE( *( v0.begin( ) + 2 ) == 7 );
+}
+
+TEST_CASE( "You can add elements to the back of a fixed size vector with "
+           "emplace_back" )
+{
+    embedded::fixed_size_vector< int, 20 > v0;
+    REQUIRE( v0.emplace_back( 5 ) );
+    REQUIRE( v0.size( ) == 1 );
+    REQUIRE( v0.capacity( ) == 20 );
+    REQUIRE( v0.emplace_back( 6 ) );
+    REQUIRE( v0.size( ) == 2 );
+    REQUIRE( v0.capacity( ) == 20 );
+    REQUIRE( v0.emplace_back( 7 ) );
+    REQUIRE( v0.size( ) == 3 );
+    REQUIRE( v0.capacity( ) == 20 );
+    REQUIRE( v0.end( ) == v0.begin( ) + v0.size( ) );
+    REQUIRE( *v0.begin( ) == 5 );
+    REQUIRE( *( v0.begin( ) + 1 ) == 6 );
+    REQUIRE( *( v0.begin( ) + 2 ) == 7 );
+}
+
+TEST_CASE( "Attempting to add elements when full returns a false value" )
+{
+    embedded::fixed_size_vector< int, 20 > v0;
+    for ( auto i = 0; i < v0.capacity( ); ++i )
+    {
+        REQUIRE( v0.push_back( i ) );
+    }
+    REQUIRE( v0.size( ) == v0.capacity( ) );
+    REQUIRE( v0.push_back( 42 ) == false );
+    REQUIRE( v0.emplace_back( 43 ) == false );
+    REQUIRE( v0.size( ) == v0.capacity( ) );
+}
+
+TEST_CASE( "You can access elements via []" )
+{
+    embedded::fixed_size_vector< int, 20 > v0;
+    for ( auto i = 0; i < v0.capacity( ); ++i )
+    {
+        REQUIRE( v0.push_back( i ) );
+    }
+    for ( auto i = 0; i < v0.capacity( ); ++i )
+    {
+        REQUIRE( v0[ i ] == i );
+    }
+}
+
+TEST_CASE( "You can pop an entry off of the end of a vector" )
+{
+    embedded::fixed_size_vector< int, 20 > v0;
+    for ( auto i = 0; i < v0.capacity( ); ++i )
+    {
+        REQUIRE( v0.push_back( i ) );
+    }
+    REQUIRE( v0.end( ) == v0.begin( ) + v0.capacity( ) );
+    REQUIRE( v0.size( ) == v0.capacity( ) );
+
+    v0.pop_back( );
+    REQUIRE( v0.end( ) == v0.begin( ) + v0.capacity( ) - 1 );
+    REQUIRE( v0.size( ) == v0.capacity( ) - 1 );
+}
+
+TEST_CASE( "You can clear a fixed sized vector to empty it" )
+{
+    embedded::fixed_size_vector< int, 20 > v0;
+    for ( auto i = 0; i < v0.capacity( ); ++i )
+    {
+        REQUIRE( v0.push_back( i ) );
+    }
+    REQUIRE( v0.end( ) == v0.begin( ) + v0.capacity( ) );
+    REQUIRE( v0.size( ) == v0.capacity( ) );
+
+    v0.clear( );
+    REQUIRE( v0.size( ) == 0 );
+    REQUIRE( v0.begin( ) == v0.end( ) );
+    REQUIRE( v0.capacity( ) == 20 );
+}
+
+class DestructionNotifier
+{
+public:
+    explicit DestructionNotifier( int* counter_dest ) : counter_{ counter_dest }
+    {
+    }
+    DestructionNotifier( const DestructionNotifier& ) noexcept = default;
+    DestructionNotifier&
+    operator=( const DestructionNotifier& ) noexcept = default;
+
+    ~DestructionNotifier( )
+    {
+        ( *counter_ )++;
+    }
+
+private:
+    int* counter_;
+};
+
+TEST_CASE( "Destructors are called on items in the fixed size vector when it "
+           "is destroyed or elements are popped off" )
+{
+    int counter = 0;
+
+    {
+        embedded::fixed_size_vector< DestructionNotifier, 10 > v0;
+
+        v0.emplace_back( &counter );
+        REQUIRE( !v0.empty( ) );
+        REQUIRE( counter == 0 );
+        v0.pop_back( );
+        REQUIRE( v0.empty( ) );
+        REQUIRE( counter == 1 );
+
+        counter = 0;
+        while ( v0.emplace_back( &counter ) )
+        {
+        }
+        REQUIRE( v0.size( ) == v0.capacity( ) );
+    }
+    REQUIRE( counter == 10 );
+}
+
+TEST_CASE( "We have defined enough to make the this usable by std::algorithms" )
+{
+    embedded::fixed_size_vector< int, 20 > v0;
+    v0.emplace_back( 5 );
+    v0.emplace_back( 4 );
+    v0.emplace_back( 3 );
+    v0.emplace_back( 2 );
+    v0.emplace_back( 1 );
+    std::sort( v0.begin( ), v0.end( ) );
+    REQUIRE( v0[ 0 ] == 1 );
+    REQUIRE( v0[ 1 ] == 2 );
+    REQUIRE( v0[ 2 ] == 3 );
+    REQUIRE( v0[ 3 ] == 4 );
+    REQUIRE( v0[ 4 ] == 5 );
+}
+
+TEST_CASE( "We can use front()/back() to reference the first/last elements when there are more than one.")
+{
+    embedded::fixed_size_vector<int, 20> v0;
+    v0.push_back(5);
+    REQUIRE( v0.front() == v0.back() );
+    v0.front() = 42;
+    REQUIRE( v0[0] == 42 );
+    v0.back() = 44;
+    REQUIRE( v0[0] == 44 );
+    v0.push_back(10);
+    REQUIRE( v0.front() == 44 );
+    REQUIRE( v0.back() == 10 );
+    REQUIRE( v0[0] == v0.front() );
+    REQUIRE( v0[1] == v0.back() );
+}
+
+TEST_CASE( "We can use front()/back() in a const setting as well")
+{
+    embedded::fixed_size_vector<int, 20> v0;
+    v0.push_back(5);
+
+    const auto& vconst = v0;
+    REQUIRE( vconst.front() == vconst.back() );
+}
+
+// static_assert(std::is_trivially_destructible<embedded::fixed_size_vector<int,
+// 20> >::value, "For trivial destructable types the fixed sized vector should
+// be trivially destructable");
+static_assert(
+    !std::is_trivially_destructible<
+        embedded::fixed_size_vector< DestructionNotifier, 10 > >::value,
+    "for non-trivially destructable types the fixed size vector is not "
+    "trivially destructable" );
\ No newline at end of file
diff --git a/src/epics/seq/test/test_main.cc b/src/epics/seq/test/test_main.cc
new file mode 100644
index 0000000000000000000000000000000000000000..063e87874ea57a58d37496c85910b1f6cd2c6415
--- /dev/null
+++ b/src/epics/seq/test/test_main.cc
@@ -0,0 +1,2 @@
+#define CATCH_CONFIG_MAIN
+#include "catch.hpp"
\ No newline at end of file
diff --git a/src/epics/seq/test/test_simple_range.cc b/src/epics/seq/test/test_simple_range.cc
new file mode 100644
index 0000000000000000000000000000000000000000..efe9d32164013a1b4315294b7d56b8453dfff966
--- /dev/null
+++ b/src/epics/seq/test/test_simple_range.cc
@@ -0,0 +1,18 @@
+//
+// Created by jonathan.hanks on 11/17/20.
+//
+#include "catch.hpp"
+
+#include "simple_range.hh"
+
+#include <vector>
+
+TEST_CASE("You can create a simple range from two iterators")
+{
+    using it_type = typename std::vector<int>::iterator;
+    std::vector<int> backing_store{1,2,3,4,5,6,7,8,9,0};
+
+    embedded::range<it_type> rng = embedded::make_range(backing_store.begin() + 3, backing_store.begin() + 3 + 3);
+    REQUIRE( *rng.begin() == 4 );
+    REQUIRE( *rng.end() == 7 );
+}
diff --git a/src/epics/util/lib/createEpicsMakefile.pm b/src/epics/util/lib/createEpicsMakefile.pm
index f6106a4c67773c41c592df88e957c602f6dfe2ed..a5af6a63aad6e5b407ae4f48ca68209881c995f3 100644
--- a/src/epics/util/lib/createEpicsMakefile.pm
+++ b/src/epics/util/lib/createEpicsMakefile.pm
@@ -23,7 +23,7 @@ my ($fileName) = @_;
 	}elsif ($globalsdf) {
 	print OUTME "SRC += $::rcg_src_dir/src/epics/seq/sdf_monitor.c\n";
 	} else {
-	print OUTME "SRC += $::rcg_src_dir/src/epics/seq/main.c\n";
+	print OUTME "SRC += $::rcg_src_dir/src/epics/seq/main.cc\n";
 	}
 	print OUTME "SRC += $::rcg_src_dir/src/epics/seq/sdf_file_loaded.c\n";
 	print OUTME "SRC += $::rcg_src_dir/src/drv/rfm.c\n";
diff --git a/src/include/sdf_file_loaded.h b/src/include/sdf_file_loaded.h
index 722e4d64ad28aa01dd001481d92fa8f88cd13f49..3e8cdf1ba6d82bacba52120b0537173342e87e9c 100644
--- a/src/include/sdf_file_loaded.h
+++ b/src/include/sdf_file_loaded.h
@@ -1,11 +1,19 @@
 #ifndef DAQD_TRUNK_SDF_FILE_LOADED_H
 #define DAQD_TRUNK_SDF_FILE_LOADED_H
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 // set or clear and sdf file loaded flag
 // this flag is used to trigger BURT_RESTORE
 // when the safe.snap or other sdf file is completely loaded
 
-extern int get_sdf_file_loaded();
-extern void set_sdf_file_loaded(int);
+extern int  get_sdf_file_loaded( );
+extern void set_sdf_file_loaded( int );
+
+#ifdef __cplusplus
+}
+#endif
 
 #endif // DAQD_TRUNK_SDF_FILE_LOADED_H
diff --git a/src/include/util/user/check_file_crc.h b/src/include/util/user/check_file_crc.h
new file mode 100644
index 0000000000000000000000000000000000000000..f756df0f4a579d03330fd6e59346585ca3cedec1
--- /dev/null
+++ b/src/include/util/user/check_file_crc.h
@@ -0,0 +1,56 @@
+//
+// Created by jonathan.hanks on 3/30/22.
+//
+
+#ifndef DAQD_TRUNK_CHECK_FILE_CRC_H
+#define DAQD_TRUNK_CHECK_FILE_CRC_H
+
+#include <stdio.h>
+#include <string.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "crc.h"
+
+
+/// Common routine to check file CRC.
+///	@param[in] *fName	Name of file to check.
+///	@return File CRC or -1 if file not found.
+static inline int
+checkFileCrc( const char* fName )
+{
+    char         cbuf[ 128 ];
+    char*        cp = 0;
+    int          flen = 0;
+    int          clen = 0;
+    unsigned int crc = 0;
+    FILE*        fp = NULL;
+
+    if ( !fName )
+    {
+        return -1;
+    }
+    fp = fopen( fName, "r" );
+    if ( fp == NULL )
+    {
+        return -1;
+    }
+
+    while ( ( cp = fgets( cbuf, 128, fp ) ) != NULL )
+    {
+        clen = strlen( cbuf );
+        flen += clen;
+        crc = crc_ptr( cbuf, clen, crc );
+    }
+    crc = crc_len( flen, crc );
+    fclose( fp );
+    return crc;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // DAQD_TRUNK_CHECK_FILE_CRC_H
diff --git a/src/simple_pv/CMakeLists.txt b/src/simple_pv/CMakeLists.txt
index 4a52fdc520d5df5f690f05e67a585550ee757137..130418a7c1176116cb0a67283e78f1b4a4c0e28c 100644
--- a/src/simple_pv/CMakeLists.txt
+++ b/src/simple_pv/CMakeLists.txt
@@ -1,7 +1,8 @@
 add_library(simple_pv simple_pv.cc
         simple_epics.cc
-        simple_epics_internal.cc)
+        simple_pv_internal.cc)
 target_include_directories(simple_pv PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
+target_include_directories(simple_pv PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/private)
 target_link_libraries(simple_pv PUBLIC epics::cas epics::gdd /usr/lib/epics/lib/linux-x86_64/libCom.so)
 target_requires_cpp11(simple_pv PUBLIC)
 add_library(pv::simple_pv ALIAS simple_pv)
@@ -9,3 +10,6 @@ add_library(pv::simple_pv ALIAS simple_pv)
 add_executable(test_simple_pv tests/test_simple_pv.cc)
 target_link_libraries(test_simple_pv PUBLIC simple_pv)
 target_include_directories(test_simple_pv PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
+
+add_executable(test_rw_ioc tests/test_rw_cas.cc)
+target_link_libraries(test_rw_ioc PUBLIC simple_pv)
\ No newline at end of file
diff --git a/src/simple_pv/private/simple_pv_int.hh b/src/simple_pv/private/simple_pv_int.hh
new file mode 100644
index 0000000000000000000000000000000000000000..ef7df71519162b4811b8fc4b051e307e3a74a85c
--- /dev/null
+++ b/src/simple_pv/private/simple_pv_int.hh
@@ -0,0 +1,286 @@
+//
+// Created by jonathan.hanks on 3/16/22.
+//
+
+#ifndef DAQD_TRUNK_SIMPLE_EPICS_INTERNAL_INT_HH
+#define DAQD_TRUNK_SIMPLE_EPICS_INTERNAL_INT_HH
+
+#include "simple_pv_types.hh"
+#include "simple_pv_internal.hh"
+#include <mutex>
+#include <type_traits>
+
+#include <iostream>
+
+namespace simple_epics
+{
+
+    namespace detail
+    {
+
+        /*!
+         * @brief A representation of a R/O integer in a PV
+         */
+        template < typename IntType >
+        class simpleBasicIntPV : public simplePVBase
+        {
+            static_assert( std::is_integral< IntType >::value,
+                           "Must use an interger type" );
+
+        public:
+            simpleBasicIntPV( caServer&                       server,
+                              pvBasicIntAttributes< IntType > attr )
+                : simplePVBase( ), server_{ server }, attr_{ std::move(
+                                                          attr ) },
+                  val_( ), monitored_{ false }
+            {
+                std::once_flag initted{ };
+                std::call_once(
+                    initted, []( ) { simpleBasicIntPV::setup_func_table( ); } );
+
+                val_ = new gddScalar( gddAppType_value,
+                                      ait_data_type< IntType >::value );
+                val_->unreference( );
+                set_value( *attr_.src( ) );
+            }
+            ~simpleBasicIntPV( ) override = default;
+
+            caStatus
+            read( const casCtx& ctx, gdd& prototype ) override
+            {
+                return get_func_table( ).read( *this, prototype );
+            }
+            caStatus
+            write( const casCtx& ctx, const gdd& value ) override
+            {
+                if ( attr_.mode( ) != PVMode::ReadWrite )
+                {
+                    return S_casApp_noSupport;
+                }
+                aitType newValue;
+                value.get( &newValue, ait_data_type< IntType >::value );
+                set_value( newValue );
+                *const_cast< IntType* >( attr_.src( ) ) = newValue;
+                return S_casApp_success;
+            }
+
+            void destroy( ) override{ };
+
+            aitEnum
+            bestExternalType( ) const override
+            {
+                return val_->primitiveType( );
+            }
+
+            const char*
+            getName( ) const override
+            {
+                return attr_.name( ).c_str( );
+            }
+
+            caStatus
+            interestRegister( ) override
+            {
+                monitored_ = true;
+                return S_casApp_success;
+            }
+
+            void
+            interestDelete( ) override
+            {
+                monitored_ = false;
+            }
+
+            void
+            update( ) override
+            {
+                set_value( *attr_.src( ) );
+            }
+
+        private:
+            using aitType = typename ait_data_type< IntType >::ait_type;
+
+            void
+            set_value( aitType value )
+            {
+                aitType current_value = 0;
+
+                val_->getConvert( current_value );
+                if ( current_value == value )
+                {
+                    return;
+                }
+
+                val_->putConvert( value );
+                aitTimeStamp ts = aitTimeStamp( epicsTime::getCurrent( ) );
+                val_->setTimeStamp( &ts );
+
+                aitUint16 stat = epicsAlarmNone;
+                aitUint16 sevr = epicsSevNone;
+                if ( value >= attr_.alarm_high( ) )
+                {
+                    stat = epicsAlarmHiHi;
+                    sevr = epicsSevMajor;
+                }
+                else if ( value <= attr_.alarm_low( ) )
+                {
+                    stat = epicsAlarmLoLo;
+                    sevr = epicsSevMajor;
+                }
+                else if ( value >= attr_.warn_high( ) )
+                {
+                    stat = epicsAlarmHigh;
+                    sevr = epicsSevMinor;
+                }
+                else if ( value <= attr_.warn_low( ) )
+                {
+                    stat = epicsAlarmLow;
+                    sevr = epicsSevMinor;
+                }
+                val_->setSevr( sevr );
+                val_->setStat( stat );
+
+                if ( monitored_ )
+                {
+                    casEventMask mask =
+                        casEventMask( server_.valueEventMask( ) );
+                    bool alarm_changed = ( stat != val_->getStat( ) ||
+                                           sevr != val_->getSevr( ) );
+                    if ( alarm_changed )
+                    {
+                        mask |= server_.alarmEventMask( );
+                    }
+                    postEvent( mask, *val_ );
+                }
+            }
+
+            static void
+            setup_func_table( )
+            {
+                auto install = []( const char* name,
+                                   gddAppFuncTableStatus (
+                                       simpleBasicIntPV::*handler )( gdd& ) ) {
+                    gddAppFuncTableStatus status;
+
+                    //           char error_string[100];
+
+                    status = get_func_table( ).installReadFunc( name, handler );
+                    if ( status != S_gddAppFuncTable_Success )
+                    {
+                        //                errSymLookup(status, error_string,
+                        //                sizeof(error_string));
+                        //               throw std::runtime_error(error_string);
+                        throw std::runtime_error(
+                            "Unable to initialize pv lookup table" );
+                    }
+                };
+
+                install( "units", &simpleBasicIntPV::read_attr_not_handled );
+                install( "status", &simpleBasicIntPV::read_status );
+                install( "severity", &simpleBasicIntPV::read_severity );
+                install( "maxElements",
+                         &simpleBasicIntPV::read_attr_not_handled );
+                install( "precision", &simpleBasicIntPV::read_precision );
+                install( "alarmHigh", &simpleBasicIntPV::read_alarm_high );
+                install( "alarmLow", &simpleBasicIntPV::read_alarm_low );
+                install( "alarmHighWarning",
+                         &simpleBasicIntPV::read_warn_high );
+                install( "alarmLowWarning", &simpleBasicIntPV::read_warn_low );
+                install( "maxElements",
+                         &simpleBasicIntPV::read_attr_not_handled );
+                install( "graphicHigh",
+                         &simpleBasicIntPV::read_attr_not_handled );
+                install( "graphicLow",
+                         &simpleBasicIntPV::read_attr_not_handled );
+                install( "controlHigh",
+                         &simpleBasicIntPV::read_attr_not_handled );
+                install( "controlLow",
+                         &simpleBasicIntPV::read_attr_not_handled );
+                install( "enums", &simpleBasicIntPV::read_attr_not_handled );
+                install( "menuitem", &simpleBasicIntPV::read_attr_not_handled );
+                install( "timestamp",
+                         &simpleBasicIntPV::read_attr_not_handled );
+                install( "value", &simpleBasicIntPV::read_value );
+            }
+            static gddAppFuncTable< simpleBasicIntPV >&
+            get_func_table( )
+            {
+                static gddAppFuncTable< simpleBasicIntPV > func_table;
+                return func_table;
+            }
+
+            gddAppFuncTableStatus
+            read_attr_not_handled( gdd& g )
+            {
+                return S_casApp_success;
+            }
+
+            gddAppFuncTableStatus
+            read_status( gdd& g )
+            {
+                g.putConvert( val_->getStat( ) );
+                return S_casApp_success;
+            }
+
+            gddAppFuncTableStatus
+            read_severity( gdd& g )
+            {
+                g.putConvert( val_->getSevr( ) );
+                return S_casApp_success;
+            }
+
+            gddAppFuncTableStatus
+            read_precision( gdd& g )
+            {
+                g.putConvert( 0 );
+                return S_casApp_success;
+            }
+
+            gddAppFuncTableStatus
+            read_alarm_high( gdd& g )
+            {
+                g.putConvert( attr_.alarm_high( ) );
+                return S_casApp_success;
+            }
+
+            gddAppFuncTableStatus
+            read_alarm_low( gdd& g )
+            {
+                g.putConvert( attr_.alarm_low( ) );
+                return S_casApp_success;
+            }
+
+            gddAppFuncTableStatus
+            read_warn_high( gdd& g )
+            {
+                g.putConvert( attr_.warn_high( ) );
+                return S_casApp_success;
+            }
+
+            gddAppFuncTableStatus
+            read_warn_low( gdd& g )
+            {
+                g.putConvert( attr_.warn_low( ) );
+                return S_casApp_success;
+            }
+
+            gddAppFuncTableStatus
+            read_value( gdd& g )
+            {
+                auto status = gddApplicationTypeTable::app_table.smartCopy(
+                    &g, val_.get( ) );
+                return ( status ? S_cas_noConvert : S_casApp_success );
+            }
+
+            caServer&                       server_;
+            pvBasicIntAttributes< IntType > attr_;
+            smartGDDPointer                 val_;
+            bool                            monitored_;
+        };
+        using simpleIntPV = simpleBasicIntPV< std::int32_t >;
+        using simpleUIntPV = simpleBasicIntPV< std::uint32_t >;
+        using simpleUShortPV = simpleBasicIntPV< std::uint16_t >;
+    } // namespace detail
+} // namespace simple_epics
+
+#endif // DAQD_TRUNK_SIMPLE_EPICS_INTERNAL_INT_HH
diff --git a/src/simple_pv/simple_epics_internal.hh b/src/simple_pv/private/simple_pv_internal.hh
similarity index 69%
rename from src/simple_pv/simple_epics_internal.hh
rename to src/simple_pv/private/simple_pv_internal.hh
index f3adeb4eedfa9b400918c8788f349c35e45a1e29..0ad59337988cd104096b721f69a9e70c4e3401ee 100644
--- a/src/simple_pv/simple_epics_internal.hh
+++ b/src/simple_pv/private/simple_pv_internal.hh
@@ -1,19 +1,20 @@
 //
-// Created by jonathan.hanks on 1/6/20.
+// Created by jonathan.hanks on 3/16/22.
 //
 
 #ifndef DAQD_TRUNK_SIMPLE_PV_INTERNAL_HH
 #define DAQD_TRUNK_SIMPLE_PV_INTERNAL_HH
 
-#include "simple_epics.hh"
 #include <memory>
 
+#include "simple_epics.hh"
+#include "simple_pv_int.hh"
+
 namespace simple_epics
 {
 
     namespace detail
     {
-
         // std::make_unique didn't make it into C++11, so
         // to allow this to work in a pre C++14 world, we
         // provide a simple replacement.
@@ -36,80 +37,6 @@ namespace simple_epics
                 new T( std::forward< Ts >( params )... ) );
         }
 
-        class setup_int_pv_table;
-
-        /*!
-         * @brief A representation of a R/O integer in a PV
-         */
-        class simpleIntPV : public simplePVBase
-        {
-            friend class setup_int_pv_table;
-
-        public:
-            simpleIntPV( caServer& server, pvIntAttributes attr )
-                : simplePVBase( ), server_{ server }, attr_{ std::move(
-                                                          attr ) },
-                  val_( ), monitored_{ false }
-            {
-                val_ = new gddScalar( gddAppType_value, aitEnumInt32 );
-                val_->unreference( );
-                set_value( *attr_.src( ) );
-            }
-            ~simpleIntPV( ) override;
-
-            caStatus read( const casCtx& ctx, gdd& prototype ) override;
-            caStatus write( const casCtx& ctx, const gdd& value ) override;
-
-            void destroy( ) override{};
-
-            aitEnum bestExternalType( ) const override;
-
-            const char*
-            getName( ) const override
-            {
-                return attr_.name( ).c_str( );
-            }
-
-            caStatus interestRegister( ) override;
-
-            void interestDelete( ) override;
-
-            void update( ) override;
-
-        private:
-            void set_value( int value );
-
-            static void                            setup_func_table( );
-            static gddAppFuncTable< simpleIntPV >& get_func_table( );
-
-            gddAppFuncTableStatus
-            read_attr_not_handled( gdd& g )
-            {
-                return S_casApp_success;
-            }
-
-            gddAppFuncTableStatus read_status( gdd& g );
-
-            gddAppFuncTableStatus read_severity( gdd& g );
-
-            gddAppFuncTableStatus read_precision( gdd& g );
-
-            gddAppFuncTableStatus read_alarm_high( gdd& g );
-
-            gddAppFuncTableStatus read_alarm_low( gdd& g );
-
-            gddAppFuncTableStatus read_warn_high( gdd& g );
-
-            gddAppFuncTableStatus read_warn_low( gdd& g );
-
-            gddAppFuncTableStatus read_value( gdd& g );
-
-            caServer&       server_;
-            pvIntAttributes attr_;
-            smartGDDPointer val_;
-            bool            monitored_;
-        };
-
         class setup_string_pv_table;
         /*!
          * @brief A representation of a R/O integer in a PV
@@ -133,7 +60,7 @@ namespace simple_epics
             caStatus read( const casCtx& ctx, gdd& prototype ) override;
             caStatus write( const casCtx& ctx, const gdd& value ) override;
 
-            void destroy( ) override{};
+            void destroy( ) override{ };
 
             aitEnum bestExternalType( ) const override;
 
@@ -199,7 +126,7 @@ namespace simple_epics
             caStatus read( const casCtx& ctx, gdd& prototype ) override;
             caStatus write( const casCtx& ctx, const gdd& value ) override;
 
-            void destroy( ) override{};
+            void destroy( ) override{ };
 
             aitEnum bestExternalType( ) const override;
 
@@ -248,9 +175,7 @@ namespace simple_epics
             smartGDDPointer    val_;
             bool               monitored_;
         };
-
     } // namespace detail
-
 } // namespace simple_epics
 
 #endif // DAQD_TRUNK_SIMPLE_PV_INTERNAL_HH
diff --git a/src/simple_pv/private/simple_pv_types.hh b/src/simple_pv/private/simple_pv_types.hh
new file mode 100644
index 0000000000000000000000000000000000000000..4e698ab7cfd01e3b9320c3a31746f1730e90adf3
--- /dev/null
+++ b/src/simple_pv/private/simple_pv_types.hh
@@ -0,0 +1,97 @@
+//
+// Created by jonathan.hanks on 3/16/22.
+//
+
+#ifndef DAQD_TRUNK_SIMPLE_PV_TYPES_HH
+#define DAQD_TRUNK_SIMPLE_PV_TYPES_HH
+
+#include <cstdint>
+#include <string>
+#include <type_traits>
+#include "aitTypes.h"
+
+namespace simple_epics
+{
+    namespace detail
+    {
+        /**
+         * @brief ait_data_type is used to map between C++ types and the
+         * CAS ait (architecture independent) data types.
+         * @tparam T The type to map
+         */
+        template < typename T >
+        struct ait_data_type
+        {
+            static const aitEnum value = aitEnumInvalid;
+            using ait_type = void;
+        };
+
+        template <>
+        struct ait_data_type< std::int16_t >
+        {
+            static const aitEnum value = aitEnumInt16;
+            using ait_type = aitInt16;
+            static_assert( std::is_same< std::int16_t, ait_type >::value,
+                           "aitType and C++ types must match" );
+        };
+
+        template <>
+        struct ait_data_type< std::uint16_t >
+        {
+            static const aitEnum value = aitEnumUint16;
+            using ait_type = aitUint16;
+            static_assert( std::is_same< std::uint16_t, ait_type >::value,
+                           "aitType and C++ types must match" );
+        };
+
+        template <>
+        struct ait_data_type< std::int32_t >
+        {
+            static const aitEnum value = aitEnumInt32;
+            using ait_type = aitInt32;
+            static_assert( std::is_same< std::int32_t, ait_type >::value,
+                           "aitType and C++ types must match" );
+        };
+
+        template <>
+        struct ait_data_type< std::uint32_t >
+        {
+            static const aitEnum value = aitEnumUint32;
+            using ait_type = aitUint32;
+            static_assert( std::is_same< std::uint32_t, ait_type >::value,
+                           "aitType and C++ types must match" );
+        };
+
+        template <>
+        struct ait_data_type< float >
+        {
+            static const aitEnum value = aitEnumFloat32;
+            using ait_type = aitFloat32;
+            static_assert( std::is_same< float, ait_type >::value,
+                           "aitType and C++ types must match" );
+        };
+
+        template <>
+        struct ait_data_type< double >
+        {
+            static const aitEnum value = aitEnumFloat64;
+            using ait_type = aitFloat64;
+            static_assert( std::is_same< double, ait_type >::value,
+                           "aitType and C++ types must match" );
+        };
+
+        template <>
+        struct ait_data_type< const char* >
+        {
+            static const aitEnum value = aitEnumString;
+        };
+
+        template <>
+        struct ait_data_type< std::string >
+        {
+            static const aitEnum value = aitEnumString;
+        };
+    } // namespace detail
+} // namespace simple_epics
+
+#endif // DAQD_TRUNK_SIMPLE_PV_TYPES_HH
diff --git a/src/simple_pv/simple_epics.cc b/src/simple_pv/simple_epics.cc
index dbce6e75f4b5656c6a30e09c8e66f607983f9ec0..3f6de43397f4144c42949deeb3248adf4f77d508 100644
--- a/src/simple_pv/simple_epics.cc
+++ b/src/simple_pv/simple_epics.cc
@@ -2,7 +2,7 @@
 // Created by jonathan.hanks on 12/20/19.
 //
 #include "simple_epics.hh"
-#include "simple_epics_internal.hh"
+#include "simple_pv_internal.hh"
 
 #include <stdexcept>
 #include <algorithm>
@@ -11,6 +11,24 @@ namespace simple_epics
 {
     Server::~Server( ) = default;
 
+    void
+    Server::addPV( pvUShortAttributes attr )
+    {
+        std::lock_guard< std::mutex > l_( m_ );
+
+        auto it = pvs_.find( attr.name( ) );
+        if ( it != pvs_.end( ) )
+        {
+            throw std::runtime_error(
+                "Duplicate key insertion to the epics db" );
+        }
+        std::string name{ attr.name( ) };
+        pvs_.insert(
+            std::make_pair( std::move( name ),
+                            detail::make_unique_ptr< detail::simpleUShortPV >(
+                                *this, std::move( attr ) ) ) );
+    }
+
     void
     Server::addPV( pvIntAttributes attr )
     {
@@ -29,6 +47,24 @@ namespace simple_epics
                                 *this, std::move( attr ) ) ) );
     }
 
+    void
+    Server::addPV( pvUIntAttributes attr )
+    {
+        std::lock_guard< std::mutex > l_( m_ );
+
+        auto it = pvs_.find( attr.name( ) );
+        if ( it != pvs_.end( ) )
+        {
+            throw std::runtime_error(
+                "Duplicate key insertion to the epics db" );
+        }
+        std::string name{ attr.name( ) };
+        pvs_.insert(
+            std::make_pair( std::move( name ),
+                            detail::make_unique_ptr< detail::simpleUIntPV >(
+                                *this, std::move( attr ) ) ) );
+    }
+
     void
     Server::addPV( pvStringAttributes attr )
     {
diff --git a/src/simple_pv/simple_epics.hh b/src/simple_pv/simple_epics.hh
index 2830457d85ade894cf65bac4b3b1f28325f528ac..c6a70ede3e8a3d824156a200a4636a33c91b1883 100644
--- a/src/simple_pv/simple_epics.hh
+++ b/src/simple_pv/simple_epics.hh
@@ -9,6 +9,7 @@
 #include <memory>
 #include <mutex>
 #include <string>
+#include <type_traits>
 #include <gddAppFuncTable.h>
 #include <epicsTimer.h>
 
@@ -36,69 +37,94 @@ namespace simple_epics
 
     class Server;
 
+    enum class PVMode
+    {
+        ReadOnly = 0,
+        ReadWrite = 1,
+    };
+
     /*!
      * @brief A description of a PV, used to describe an int PV to the server.
      * @note this is given a pointer to the data.  This value is only read
      * when a Server object is told to update its data.
      */
-    class pvIntAttributes
+    template < typename IntType >
+    class pvBasicIntAttributes
     {
+        static_assert( std::is_integral< IntType >::value,
+                       "integer type required" );
+
     public:
-        pvIntAttributes( std::string           pv_name,
-                         int*                  value,
-                         std::pair< int, int > alarm_range,
-                         std::pair< int, int > warn_range )
+        using value_type = IntType;
+
+        pvBasicIntAttributes( std::string                   pv_name,
+                              IntType*                      value,
+                              std::pair< IntType, IntType > alarm_range,
+                              std::pair< IntType, IntType > warn_range,
+                              PVMode mode = PVMode::ReadOnly )
             : name_{ std::move( pv_name ) },
 
               alarm_low_{ alarm_range.first },
               alarm_high_{ alarm_range.second }, warn_low_{ warn_range.first },
-              warn_high_{ warn_range.second }, src_{ value }
+              warn_high_{ warn_range.second }, mode_{ mode }, src_{ value }
         {
         }
 
         const std::string&
-        name( ) const
+        name( ) const noexcept
         {
             return name_;
         }
 
-        int
-        alarm_high( ) const
+        IntType
+        alarm_high( ) const noexcept
         {
             return alarm_high_;
         }
-        int
-        alarm_low( ) const
+        IntType
+        alarm_low( ) const noexcept
         {
             return alarm_low_;
         }
-        int
-        warn_high( ) const
+        IntType
+        warn_high( ) const noexcept
         {
             return warn_high_;
         }
-        int
-        warn_low( ) const
+        IntType
+        warn_low( ) const noexcept
         {
             return warn_low_;
         }
 
-        const int*
-        src( ) const
+        const IntType*
+        src( ) const noexcept
         {
             return src_;
         }
 
+        PVMode
+        mode( ) const noexcept
+        {
+            return mode_;
+        }
+
     private:
         std::string name_;
 
-        int alarm_high_;
-        int alarm_low_;
-        int warn_high_;
-        int warn_low_;
+        IntType alarm_high_;
+        IntType alarm_low_;
+        IntType warn_high_;
+        IntType warn_low_;
 
-        int* src_;
+        PVMode   mode_;
+        IntType* src_{ nullptr };
     };
+    using pvIntAttributes = pvBasicIntAttributes< std::int32_t >;
+    using pvUIntAttributes = pvBasicIntAttributes< std::uint32_t >;
+    using pvUShortAttributes = pvBasicIntAttributes< std::uint16_t >;
+    static_assert( sizeof( std::int32_t ) == sizeof( int ),
+                   "int must be 32 bit" );
 
     /*!
      * @brief A description of a PV, used to describe a string PV to the server.
@@ -108,27 +134,50 @@ namespace simple_epics
     class pvStringAttributes
     {
     public:
-        pvStringAttributes( std::string pv_name, const char* value )
-            : name_{ std::move( pv_name ) }, src_{ value }
+        pvStringAttributes( std::string pv_name,
+                            const char* value,
+                            PVMode      mode = PVMode::ReadOnly,
+                            std::size_t buffer_size = 0 )
+            : name_{ std::move( pv_name ) }, mode_{ mode }, src_{ value },
+              src_size_{ buffer_size }
         {
         }
 
         const std::string&
-        name( ) const
+        name( ) const noexcept
         {
             return name_;
         }
 
         const char*
-        src( ) const
+        src( ) const noexcept
         {
             return src_;
         }
 
+        char*
+        src( ) noexcept
+        {
+            return (char*)src_;
+        }
+
+        std::size_t
+        src_size( ) const noexcept
+        {
+            return src_size_;
+        }
+
+        PVMode
+        mode( ) const noexcept
+        {
+            return mode_;
+        }
+
     private:
         std::string name_;
-
+        PVMode      mode_;
         const char* src_;
+        std::size_t src_size_;
     };
 
     /*!
@@ -142,48 +191,55 @@ namespace simple_epics
         pvDoubleAttributes( std::string                 pv_name,
                             double*                     value,
                             std::pair< double, double > alarm_range,
-                            std::pair< double, double > warn_range )
+                            std::pair< double, double > warn_range,
+                            PVMode mode = PVMode::ReadOnly )
             : name_{ std::move( pv_name ) },
 
               alarm_low_{ alarm_range.first },
               alarm_high_{ alarm_range.second }, warn_low_{ warn_range.first },
-              warn_high_{ warn_range.second }, src_{ value }
+              warn_high_{ warn_range.second }, mode_{ mode }, src_{ value }
         {
         }
 
         const std::string&
-        name( ) const
+        name( ) const noexcept
         {
             return name_;
         }
 
         double
-        alarm_high( ) const
+        alarm_high( ) const noexcept
         {
             return alarm_high_;
         }
         double
-        alarm_low( ) const
+        alarm_low( ) const noexcept
         {
             return alarm_low_;
         }
         double
-        warn_high( ) const
+        warn_high( ) const noexcept
         {
             return warn_high_;
         }
         double
-        warn_low( ) const
+        warn_low( ) const noexcept
         {
             return warn_low_;
         }
 
         const double*
-        src( ) const
+        src( ) const noexcept
         {
             return src_;
         }
 
+        PVMode
+        mode( ) const noexcept
+        {
+            return mode_;
+        }
+
     private:
         std::string name_;
 
@@ -192,7 +248,9 @@ namespace simple_epics
         double warn_high_;
         double warn_low_;
 
-        double* src_;
+        PVMode mode_;
+
+        double* src_{ nullptr };
     };
 
     /*!
@@ -201,7 +259,7 @@ namespace simple_epics
     class Server : public caServer
     {
     public:
-        Server( ) : caServer( ), pvs_{}
+        Server( ) : caServer( ), pvs_{ }
         {
         }
         ~Server( ) override;
@@ -209,7 +267,9 @@ namespace simple_epics
         /*!
          * @brief Add a PV to the server.
          */
+        void addPV( pvUShortAttributes attr );
         void addPV( pvIntAttributes attr );
+        void addPV( pvUIntAttributes attr );
         void addPV( pvStringAttributes attr );
         void addPV( pvDoubleAttributes attr );
 
diff --git a/src/simple_pv/simple_pv.cc b/src/simple_pv/simple_pv.cc
index 7ab5c2b729a59dea61e94a3d77846938063c3d15..bffe8c2a2c90e37e4131b6d4d6fcdac3327221fa 100644
--- a/src/simple_pv/simple_pv.cc
+++ b/src/simple_pv/simple_pv.cc
@@ -3,7 +3,7 @@
 //
 #include "simple_pv.h"
 #include "simple_epics.hh"
-#include "simple_epics_internal.hh"
+#include "simple_pv_internal.hh"
 
 #include <algorithm>
 #include <memory>
@@ -29,15 +29,14 @@ simple_pv_server_create( const char* prefix, SimplePV* pvs, int pv_count )
     std::for_each(
         pvs,
         pvs + pv_count,
-        [&prefix_, pv_server]( const SimplePV& pv ) -> void {
+        [ &prefix_, pv_server ]( const SimplePV& pv ) -> void {
             if ( !pv.name || !pv.data )
             {
                 return;
             }
             switch ( pv.pv_type )
             {
-            case SIMPLE_PV_INT:
-            {
+            case SIMPLE_PV_INT: {
                 pv_server->addPV( simple_epics::pvIntAttributes(
                     prefix_ + pv.name,
                     reinterpret_cast< int* >( pv.data ),
@@ -45,14 +44,12 @@ simple_pv_server_create( const char* prefix, SimplePV* pvs, int pv_count )
                     std::make_pair< int, int >( pv.warn_low, pv.warn_high ) ) );
             }
             break;
-            case SIMPLE_PV_STRING:
-            {
+            case SIMPLE_PV_STRING: {
                 pv_server->addPV( simple_epics::pvStringAttributes(
                     prefix_ + pv.name, reinterpret_cast< char* >( pv.data ) ) );
             }
             break;
-            case SIMPLE_PV_DOUBLE:
-            {
+            case SIMPLE_PV_DOUBLE: {
                 pv_server->addPV( simple_epics::pvDoubleAttributes(
                     prefix_ + pv.name,
                     reinterpret_cast< double* >( pv.data ),
diff --git a/src/simple_pv/simple_epics_internal.cc b/src/simple_pv/simple_pv_internal.cc
similarity index 64%
rename from src/simple_pv/simple_epics_internal.cc
rename to src/simple_pv/simple_pv_internal.cc
index 2bf9c4a17636319d2df670ce52d9c6d346dbe474..adf5909f8e30c7e9ecc3a0336f765a7726378ddf 100644
--- a/src/simple_pv/simple_epics_internal.cc
+++ b/src/simple_pv/simple_pv_internal.cc
@@ -1,24 +1,13 @@
 //
 // Created by jonathan.hanks on 1/6/20.
 //
-#include "simple_epics_internal.hh"
+#include "simple_pv_internal.hh"
 #include <cstring>
 
 namespace simple_epics
 {
     namespace detail
     {
-        class setup_int_pv_table
-        {
-        public:
-            setup_int_pv_table( )
-            {
-                simpleIntPV::setup_func_table( );
-            }
-        };
-
-        setup_int_pv_table pv_int_table_setup_;
-
         class setup_string_pv_table
         {
         public:
@@ -41,206 +30,6 @@ namespace simple_epics
 
         setup_double_pv_table pv_double_table_setup_;
 
-        /*
-         * Start of int PV
-         */
-
-        gddAppFuncTable< simpleIntPV >&
-        simpleIntPV::get_func_table( )
-        {
-            static gddAppFuncTable< simpleIntPV > func_table;
-            return func_table;
-        }
-
-        simpleIntPV::~simpleIntPV( ) = default;
-
-        gddAppFuncTableStatus
-        simpleIntPV::read_status( gdd& g )
-        {
-            g.putConvert( val_->getStat( ) );
-            return S_casApp_success;
-        }
-
-        gddAppFuncTableStatus
-        simpleIntPV::read_severity( gdd& g )
-        {
-            g.putConvert( val_->getSevr( ) );
-            return S_casApp_success;
-        }
-
-        gddAppFuncTableStatus
-        simpleIntPV::read_precision( gdd& g )
-        {
-            g.putConvert( 0 );
-            return S_casApp_success;
-        }
-
-        gddAppFuncTableStatus
-        simpleIntPV::read_alarm_high( gdd& g )
-        {
-            g.putConvert( attr_.alarm_high( ) );
-            return S_casApp_success;
-        }
-
-        gddAppFuncTableStatus
-        simpleIntPV::read_alarm_low( gdd& g )
-        {
-            g.putConvert( attr_.alarm_low( ) );
-            return S_casApp_success;
-        }
-
-        gddAppFuncTableStatus
-        simpleIntPV::read_warn_high( gdd& g )
-        {
-            g.putConvert( attr_.warn_high( ) );
-            return S_casApp_success;
-        }
-
-        gddAppFuncTableStatus
-        simpleIntPV::read_warn_low( gdd& g )
-        {
-            g.putConvert( attr_.warn_low( ) );
-            return S_casApp_success;
-        }
-
-        gddAppFuncTableStatus
-        simpleIntPV::read_value( gdd& g )
-        {
-            auto status =
-                gddApplicationTypeTable::app_table.smartCopy( &g, val_.get( ) );
-            return ( status ? S_cas_noConvert : S_casApp_success );
-        }
-
-        void
-        simpleIntPV::setup_func_table( )
-        {
-            auto install =
-                []( const char* name,
-                    gddAppFuncTableStatus ( simpleIntPV::*handler )( gdd& ) ) {
-                    gddAppFuncTableStatus status;
-
-                    //           char error_string[100];
-
-                    status = get_func_table( ).installReadFunc( name, handler );
-                    if ( status != S_gddAppFuncTable_Success )
-                    {
-                        //                errSymLookup(status, error_string,
-                        //                sizeof(error_string));
-                        //               throw std::runtime_error(error_string);
-                        throw std::runtime_error(
-                            "Unable to initialize pv lookup table" );
-                    }
-                };
-
-            install( "units", &simpleIntPV::read_attr_not_handled );
-            install( "status", &simpleIntPV::read_status );
-            install( "severity", &simpleIntPV::read_severity );
-            install( "maxElements", &simpleIntPV::read_attr_not_handled );
-            install( "precision", &simpleIntPV::read_precision );
-            install( "alarmHigh", &simpleIntPV::read_alarm_high );
-            install( "alarmLow", &simpleIntPV::read_alarm_low );
-            install( "alarmHighWarning", &simpleIntPV::read_warn_high );
-            install( "alarmLowWarning", &simpleIntPV::read_warn_low );
-            install( "maxElements", &simpleIntPV::read_attr_not_handled );
-            install( "graphicHigh", &simpleIntPV::read_attr_not_handled );
-            install( "graphicLow", &simpleIntPV::read_attr_not_handled );
-            install( "controlHigh", &simpleIntPV::read_attr_not_handled );
-            install( "controlLow", &simpleIntPV::read_attr_not_handled );
-            install( "enums", &simpleIntPV::read_attr_not_handled );
-            install( "menuitem", &simpleIntPV::read_attr_not_handled );
-            install( "timestamp", &simpleIntPV::read_attr_not_handled );
-            install( "value", &simpleIntPV::read_value );
-        }
-
-        caStatus
-        simpleIntPV::read( const casCtx& ctx, gdd& prototype )
-        {
-            return get_func_table( ).read( *this, prototype );
-        }
-        caStatus
-        simpleIntPV::write( const casCtx& ctx, const gdd& value )
-        {
-            return S_casApp_noSupport;
-        }
-
-        aitEnum
-        simpleIntPV::bestExternalType( ) const
-        {
-            return val_->primitiveType( );
-        }
-
-        caStatus
-        simpleIntPV::interestRegister( )
-        {
-            monitored_ = true;
-            return S_casApp_success;
-        }
-
-        void
-        simpleIntPV::interestDelete( )
-        {
-            monitored_ = false;
-        }
-
-        void
-        simpleIntPV::update( )
-        {
-            set_value( *attr_.src( ) );
-        }
-
-        void
-        simpleIntPV::set_value( int value )
-        {
-            int current_value = 0;
-
-            val_->getConvert( current_value );
-            if ( current_value == value )
-            {
-                return;
-            }
-
-            val_->putConvert( value );
-            aitTimeStamp ts = aitTimeStamp( epicsTime::getCurrent( ) );
-            val_->setTimeStamp( &ts );
-
-            aitUint16 stat = epicsAlarmNone;
-            aitUint16 sevr = epicsSevNone;
-            if ( value >= attr_.alarm_high( ) )
-            {
-                stat = epicsAlarmHiHi;
-                sevr = epicsSevMajor;
-            }
-            else if ( value <= attr_.alarm_low( ) )
-            {
-                stat = epicsAlarmLoLo;
-                sevr = epicsSevMajor;
-            }
-            else if ( value >= attr_.warn_high( ) )
-            {
-                stat = epicsAlarmHigh;
-                sevr = epicsSevMinor;
-            }
-            else if ( value <= attr_.warn_low( ) )
-            {
-                stat = epicsAlarmLow;
-                sevr = epicsSevMinor;
-            }
-            val_->setSevr( sevr );
-            val_->setStat( stat );
-
-            if ( monitored_ )
-            {
-                casEventMask mask = casEventMask( server_.valueEventMask( ) );
-                bool         alarm_changed =
-                    ( stat != val_->getStat( ) || sevr != val_->getSevr( ) );
-                if ( alarm_changed )
-                {
-                    mask |= server_.alarmEventMask( );
-                }
-                postEvent( mask, *val_ );
-            }
-        }
-
         /*
          * Start of string PV
          */
@@ -334,7 +123,20 @@ namespace simple_epics
         caStatus
         simpleStringPV::write( const casCtx& ctx, const gdd& value )
         {
-            return S_casApp_noSupport;
+            if ( attr_.mode( ) != PVMode::ReadWrite || attr_.src_size( ) < 1 )
+            {
+                return S_casApp_noSupport;
+            }
+            aitString newValue;
+            value.get( &newValue, aitEnumString );
+            if ( newValue.string( ) == nullptr )
+            {
+                return S_casApp_noSupport;
+            }
+            set_value( newValue.string( ) );
+            std::strncpy( attr_.src( ), newValue.string( ), attr_.src_size( ) );
+            attr_.src( )[ attr_.src_size( ) - 1 ] == '\0';
+            return S_casApp_success;
         }
 
         aitEnum
@@ -505,7 +307,15 @@ namespace simple_epics
         caStatus
         simpleDoublePV::write( const casCtx& ctx, const gdd& value )
         {
-            return S_casApp_noSupport;
+            if ( attr_.mode( ) != PVMode::ReadWrite )
+            {
+                return S_casApp_noSupport;
+            }
+            aitFloat64 newValue;
+            value.get( &newValue, aitEnumFloat64 );
+            set_value( newValue );
+            *const_cast< double* >( attr_.src( ) ) = newValue;
+            return S_casApp_success;
         }
 
         aitEnum
diff --git a/src/simple_pv/tests/test_rw_cas.cc b/src/simple_pv/tests/test_rw_cas.cc
new file mode 100644
index 0000000000000000000000000000000000000000..260eb80bcbb04f278be7f89c46d48a8571278c1e
--- /dev/null
+++ b/src/simple_pv/tests/test_rw_cas.cc
@@ -0,0 +1,52 @@
+//
+// Created by jonathan.hanks on 3/14/22.
+//
+#include "simple_epics.hh"
+#include <atomic>
+#include <csignal>
+
+#include "fdManager.h"
+
+std::atomic< bool > done{ false };
+
+void
+signal_handler( int dummy )
+{
+    done = true;
+}
+
+int
+main( int argc, char* argv[] )
+{
+    using pvInt = simple_epics::pvIntAttributes;
+    simple_epics::Server server{ };
+
+    int                              int1val = 0;
+    pvInt                            int1{ "int1",
+                &int1val,
+                std::make_pair( -1, 100 ),
+                std::make_pair( -1, 100 ),
+                simple_epics::PVMode::ReadWrite };
+    double                           float1val = 0.0;
+    simple_epics::pvDoubleAttributes float1{ "float1",
+                                             &float1val,
+                                             std::make_pair( -1, 100 ),
+                                             std::make_pair( -1, 100 ),
+                                             simple_epics::PVMode::ReadWrite };
+
+    server.addPV( int1 );
+    server.addPV( float1 );
+
+    std::signal( SIGINT, signal_handler );
+    server.update( );
+    while ( !done )
+    {
+        if ( int1val == 42 )
+        {
+            done = true;
+        }
+        server.update( );
+        fileDescriptorManager.process( 0. );
+    }
+    return 0;
+}
\ No newline at end of file