Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 7 additions & 16 deletions FileContentsServerApp/Db/FileContentsServer.db
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,21 @@ record(waveform, "$(P)LINES_ARRAY:SP")
}


record(stringin, "$(P)FILE_NAME")
record(stringin , "$(P)INPUT_FILE")
{
field(DTYP, "asynOctetRead")
field(INP, "@asyn($(PORT),0,0)FILE_NAME")
field(INP, "@asyn($(PORT),0,0)INPUT_FILE")
field(SCAN, "I/O Intr")
}

record(stringout, "$(P)FILE_NAME:SP")
record(stringin, "$(P)OUTPUT_FILE")
{
field(DTYP, "asynOctetWrite")
field(OUT, "@asyn($(PORT),0,0)FILE_NAME")
info(autosaveFields, "VAL")
field(PINI, "YES")
field(DTYP, "asynOctetRead")
field(INP, "@asyn($(PORT),0,0)OUTPUT_FILE")
field(SCAN, "I/O Intr")
}


record(bo, "$(P)SAVE_FILE")
{
field(DESC, "Trigger save file")
Expand Down Expand Up @@ -64,15 +64,6 @@ record(bo, "$(P)RESET")
field(UDFS, "NO_ALARM")
}

record(bi, "$(P)NEW_FILE_WARNING"){
field(DTYP, "asynInt32")
field(INP, "@asyn($(PORT),0,0)NEW_FILE_WARNING")
field(VAL, "0")
field(ZNAM, "No")
field(ONAM, "Yes")
field(SCAN, "I/O Intr")
}

record(bi, "$(P)UNSAVED_CHANGES"){
field(DTYP, "asynInt32")
field(INP, "@asyn($(PORT),0,0)UNSAVED_CHANGES")
Expand Down
166 changes: 90 additions & 76 deletions FileContentsServerApp/src/FileContentsServerDriver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,13 @@
#include <math.h>
#include <exception>
#include <iostream>
#include <format>
#include <map>
#include <list>
#include <vector>
#include <iomanip>
#include <fstream>
#include <sys/timeb.h>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/join.hpp>
#include <sstream>

#include <epicsTypes.h>
Expand Down Expand Up @@ -40,9 +39,9 @@

static const char *driverName = "FileContentsServerDriver";

FileContentsServerDriver::FileContentsServerDriver(const char *portName, const char *fileDir)
FileContentsServerDriver::FileContentsServerDriver(const char *portName, const char *fileDir, const char *inputFile, const char *outputFile)
: asynPortDriver(portName,
0, /* maxAddr */
0, /* maxAddr */
asynInt32Mask | asynInt32ArrayMask | asynFloat64Mask | asynFloat64ArrayMask | asynOctetMask | asynDrvUserMask, /* Interface mask */
asynInt32Mask | asynInt32ArrayMask | asynFloat64Mask | asynFloat64ArrayMask | asynOctetMask, /* Interrupt mask */
ASYN_CANBLOCK, /* asynFlags. This driver can block but it is not multi-device */
Expand All @@ -52,30 +51,85 @@ FileContentsServerDriver::FileContentsServerDriver(const char *portName, const c
m_fileDir(fileDir)
{
const char *functionName = "FileContentsServerDriver";
createParam(P_fileNameString, asynParamOctet, &P_fileName);

createParam(P_inputFileNameString, asynParamOctet, &P_inputFileName);
createParam(P_outputFileNameString, asynParamOctet, &P_outputFileName);
createParam(P_fileContentsString, asynParamOctet, &P_fileContents);
createParam(P_saveFileString, asynParamInt32, &P_saveFile);
createParam(P_resetString, asynParamInt32, &P_reset);
createParam(P_fileDirString, asynParamOctet, &P_fileDir);
createParam(P_logString, asynParamOctet, &P_log);
createParam(P_newFileWarningString, asynParamInt32, &P_newFileWarning);
createParam(P_unsavedChangesString, asynParamInt32, &P_unsavedChanges);

setStringParam(P_fileDir, fileDir);
setStringParam(P_inputFileName, inputFile);

// Read the original contents in first.
readFile();

if (outputFile && *outputFile)
{
setStringParam(P_outputFileName, outputFile);
// Save the file so that the output file has the contents of the input file initially.
saveFile();
}
else
{
// If the output file is not specified, default to saving output to the input file.
setStringParam(P_outputFileName, inputFile);
}

const int defaultSaveFile = 0;
setIntegerParam(P_saveFile, defaultSaveFile);
const int defaultReset = 0;
setIntegerParam(P_reset, defaultReset);
const int defaultNewFileWarning = 0;
setIntegerParam(P_newFileWarning, defaultNewFileWarning);
const int defaultUnsavedChanges = 0;
setIntegerParam(P_unsavedChanges, defaultUnsavedChanges);
setStringParam(P_fileContents, "");
setStringParam(P_log, "");
logMessage("Editor initialized");
callParamCallbacks();
}

asynStatus FileContentsServerDriver::saveFile()
{
logMessage("Triggering save");

std::string buffer;

// Get the value of the parameter we just set
getStringParam(P_fileContents, buffer);

std::string fileName;
getStringParam(P_outputFileName, fileName);
// join the file name with the file directory
std::string fullFileName = m_fileDir + "/" + fileName;
// Write the contents of buffer to a file
try
{
std::ofstream outfile(fullFileName, std::ios::trunc | std::ios::binary); // Open in write mode
Comment thread
rerpha marked this conversation as resolved.
if (outfile.is_open())
{
outfile << buffer;
outfile.close();
logMessage("File saved successfully");
setIntegerParam(P_saveFile, 0);
setIntegerParam(P_unsavedChanges, 0);
}
else
{
logMessage(std::format("Failed to open file ({}) for writing", fullFileName));
return asynError;
}
}
catch (const std::exception &e)
{
logMessage(e.what());
return asynError;
}
callParamCallbacks();
return asynSuccess;
}

// Override the method to handle writes to parameters
asynStatus FileContentsServerDriver::writeInt32(asynUser *pasynUser, epicsInt32 value)
{
Expand All @@ -85,55 +139,17 @@ asynStatus FileContentsServerDriver::writeInt32(asynUser *pasynUser, epicsInt32
{
if (value == 1 && hasChanged == 1)
{
std::cout << "Triggering save" << std::endl;
std::cout << "Value received by asyn: " << value << std::endl;

char buffer[5000]; // Assuming the response will fit within 256 characters

// Get the value of the parameter we just set
getStringParam(P_fileContents, sizeof(buffer), buffer);

char fileName[512];
getStringParam(P_fileName, sizeof(fileName), fileName);
// join the file name with the file directory
std::string m_fullFileName = m_fileDir + "/" + fileName;
// Write the contents of buffer to a file
try
{
std::ofstream outfile(m_fullFileName, std::ios::trunc | std::ios::binary); // Open in write mode
if (outfile.is_open())
{
outfile << buffer;
outfile.close();
std::cout << "Contents written to file successfully." << std::endl;
logMessage("File saved successfully");
setIntegerParam(P_saveFile, 0);
setIntegerParam(P_newFileWarning, 0);
setIntegerParam(P_unsavedChanges, 0);
}
else
{
std::cerr << "Failed to open file for writing." << std::endl;
logMessage("Failed to open file for writing");
return asynError;
}
}
catch (const std::exception &e)
{
std::cerr << "Exception caught: " << e.what() << std::endl;
logMessage(e.what());
return asynError;
}
callParamCallbacks();
return saveFile();
}
return asynSuccess;
}
else if (pasynUser->reason == P_reset)
{
if (value == 1)
{
std::cout << "Resetting" << std::endl;
logMessage("Reloading from disk");
setStringParam(P_fileContents, m_original_lines_array);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't actually reloading from disk, it is resetting to what was there the first time it loaded from disk. Is that what the users would expect?

saveFile();
logMessage("Reset successful.");
setIntegerParam(P_reset, 0);
setIntegerParam(P_unsavedChanges, 0);
Expand All @@ -147,7 +163,7 @@ asynStatus FileContentsServerDriver::writeInt32(asynUser *pasynUser, epicsInt32
}
}

void FileContentsServerDriver::logMessage(const std::string& message)
void FileContentsServerDriver::logMessage(const std::string &message)
{
// add the time to the front of the log message
// current time
Expand All @@ -162,38 +178,38 @@ void FileContentsServerDriver::logMessage(const std::string& message)
// Create the log message
std::string log_message = std::string(time_str) + ": " + message;

std::cout << log_message << std::endl;
setStringParam(P_log, log_message);
callParamCallbacks();
}

void FileContentsServerDriver::readFile()
{
std::cout << "Calling readFile" << std::endl;
std::fstream f;
char fileName[256];
getStringParam(P_fileName, sizeof(fileName), fileName);
std::string m_fullFileName = m_fileDir + "/" + fileName;
std::cout << "FileContentsServerDriver: Reading file " << m_fullFileName << std::endl;
std::ifstream f;
std::string fileName;
getStringParam(P_inputFileName, fileName);
std::string fullFileName = m_fileDir + "/" + fileName;

logMessage(std::format("Reading file {}", fullFileName));
try
{
f.open(m_fullFileName, std::ios::in);
f.open(fullFileName, std::ios::binary);
if (!f.is_open())
{
m_original_lines_array = "";
setStringParam(P_fileContents, "");
if (errno == ENOENT) // File not found
{
std::cout << "File not found: " + m_fullFileName << std::endl;
setIntegerParam(P_newFileWarning, 1);
throw std::runtime_error("File not found");
logMessage(std::format("File not found: {}", fullFileName));
throw std::runtime_error("File not found");
}
else // Other errors, e.g., permission denied
{
std::cerr << "Unable to open file: " + m_fullFileName << std::endl;
throw std::runtime_error(std::string("Unable to open file: ") + std::string(strerror(errno)));
logMessage(std::format("Unable to open file: {}, error {}", fullFileName, std::string(strerror(errno))));
throw std::runtime_error(std::string("Unable to open file: ") + std::string(strerror(errno)));
}
}
setIntegerParam(P_newFileWarning, 0);
int i = 0;

std::stringstream buffer;
Expand All @@ -209,7 +225,7 @@ void FileContentsServerDriver::readFile()
catch (const std::exception &e)
{
std::cerr << "Exception caught: " << e.what() << std::endl;
logMessage(e.what());
logMessage(e.what());
}

callParamCallbacks();
Expand All @@ -235,12 +251,6 @@ asynStatus FileContentsServerDriver::writeOctet(asynUser *pasynUser, const char
setIntegerParam(P_unsavedChanges, 0);
}
}
else if (function == P_fileName)
{
std::cout << "Setting file name to " << value << std::endl;
setStringParam(P_fileName, value);
readFile();
}
else
{
return asynPortDriver::writeOctet(pasynUser, value, maxChars, nActual);
Expand All @@ -259,11 +269,11 @@ extern "C"

/// \param[in] portName @copydoc initArg0
/// \param[in] fileDir @copydoc initArg1
int FileContentsServerConfigure(const char *portName, const char *fileDir)
int FileContentsServerConfigure(const char *portName, const char *fileDir, const char *inputFile, const char *outputFile)
{
try
{
FileContentsServerDriver *driver = new FileContentsServerDriver(portName, fileDir);
FileContentsServerDriver *driver = new FileContentsServerDriver(portName, fileDir, inputFile, outputFile);
if (driver == NULL)
{
errlogSevPrintf(errlogMajor, "FileContentsServerConfigure failed (NULL)\n");
Expand All @@ -283,17 +293,21 @@ extern "C"

// EPICS iocsh shell commands

static const iocshArg initArg0 = {"portName", iocshArgString}; ///< The name of the asyn driver port we will create
static const iocshArg initArg1 = {"fileDir", iocshArgString}; ///< file name
static const iocshArg initArg0 = {"portName", iocshArgString}; ///< The name of the asyn driver port we will create
static const iocshArg initArg1 = {"fileDir", iocshArgString}; ///< The directory to open/save files into
static const iocshArg initArg2 = {"inputFile", iocshArgString}; ///< input file name to read and initially serve contents of
static const iocshArg initArg3 = {"outputFile", iocshArgString}; ///< file name to save output to when a user hits save. If not set this defaults to inputFile.

static const iocshArg *const initArgs[] = {&initArg0,
&initArg1};
&initArg1,
&initArg2,
&initArg3};

static const iocshFuncDef initFuncDef = {"FileContentsServerConfigure", sizeof(initArgs) / sizeof(iocshArg *), initArgs};

static void initCallFunc(const iocshArgBuf *args)
{
FileContentsServerConfigure(args[0].sval, args[1].sval);
FileContentsServerConfigure(args[0].sval, args[1].sval, args[2].sval, args[3].sval);
}

static void FileContentsServerRegister(void)
Expand Down
15 changes: 7 additions & 8 deletions FileContentsServerApp/src/FileContentsServerDriver.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,38 +6,37 @@
class FileContentsServerDriver : public asynPortDriver
{
public:
FileContentsServerDriver(const char *portName, const char *fileDir);
FileContentsServerDriver(const char *portName, const char *fileDir, const char *inputFile, const char *outputFile);

// These are the methods that we override from asynPortDriver
virtual asynStatus writeOctet(asynUser *pasynUser, const char *value, size_t maxChars, size_t *nActual);
virtual asynStatus writeInt32(asynUser *pasynUser, epicsInt32 value);

private:
int P_fileName;
int P_inputFileName;
int P_outputFileName;
int P_fileContents;
int P_saveFile;
int P_fileDir;
int P_reset;
int P_log;
int P_newFileWarning;
int P_unsavedChanges;
#define FIRST_FILESERV_PARAM P_fileName

std::string m_fileName;
std::string m_fileDir;
std::string m_original_lines_array;

void readFile();
void logMessage(const std::string& message);
asynStatus saveFile();
void logMessage(const std::string &message);
};

#define P_fileNameString "FILE_NAME"
#define P_inputFileNameString "INPUT_FILE"
#define P_outputFileNameString "OUTPUT_FILE"
#define P_fileContentsString "LINES_ARRAY"
#define P_saveFileString "SAVE_FILE"
#define P_fileDirString "FILE_DIR"
#define P_logString "LOG"
#define P_resetString "RESET"
#define P_newFileWarningString "NEW_FILE_WARNING"
#define P_unsavedChangesString "UNSAVED_CHANGES"

#endif /* FILECONTENTSSERVERDRIVER_H */
1 change: 1 addition & 0 deletions FileContentsServerApp/src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ include $(TOP)/configure/CONFIG

USR_INCLUDES += -I"$(TOP)/../../libraries/boost/include"
#USR_CXXFLAGS += /EHa /DNOMINMAX
USR_CXXFLAGS += /std:c++20

LIBRARY_IOC += FileContentsServer
FileContentsServer_SRCS += FileContentsServerDriver.cpp
Expand Down