|
The code I am using is attached.
Thank you. It still seems to compile after adding the override keyword, but not get called.
On 8/19/25 10:21, Thomas, Patrick via Tech-talk wrote:
Hello,
I'm working through implementing a class that inherits from asynPortDriver and attempting to override the 'connect' virtual method. However, the code I have overridden it with never appears to be called. Is there a configuration parameter or something else
that I need to do to have this work?
This might be a simple c++ issue with attempting to override a method, but providing an incorrect argument list. With C++11, the "override" keyword can help detect this.
http ://www.en.cppreference.com/w/cpp/language/override.html
Thank you,
Patrick
Hi Mark,
I did see that and was starting to look at using asyn, but I was having trouble figuring out how to do so. Is there a minimal working example that I might be able to modify? Do I need to create separate driver and device support?
The other thought I had was if this could be handled by somehow creating record support for read/write records.
Thank you,
Patrick
Hi Patrick,
As Ralph said, if your driver is written using asyn then you can use the info(asyn:READBACK) tag on your output record.
I'm not sure what you mean here. I think the EPICS record locking an asyn port locking will guarantee that all EPICS writes get sent to the hardware atomically, and that all callbacks from the driver that update the output record will be atomic. However,
once EPICS writes the value to the hardware, the next poll of the hardware could quickly change the output record. Maybe I am not understanding your question.
Mark
Hello,
I'm attempting to write device support to handle the scenario where changes to the value of a hardware device can be made from both EPICS and another source. I would like the value of the hardware to be changed if I write a value to the record through channel
access, and also to continuously monitor the hardware device for changes made from the other source and update the value of the record to match. One approach I have considered is to use separate output and input records, where the output records write values
set from channel access to the hardware and the input records periodically scan the hardware and update their values accordingly. I am wondering however if there is a way to do this with the val field of just an output record. Is there any way to synchronize
things so that write requests coming from channel access are always written to the hardware and not overridden by periodic updates from reading the hardware?
Thank you,
Patrick
|
#ifndef ADS_ASYN_PORT_DRIVER_HPP
#define ADS_ASYN_PORT_DRIVER_HPP
#include "initHooks.h"
#include "asynPortDriver.h"
#include "devADS.hpp"
class adsAsynPortDriver : public asynPortDriver {
public:
adsAsynPortDriver(std::string portName, AmsNetId ams_address_net_id, uint16_t ams_address_port, std::string ip_address_string);
//virtual ~adsAsynPortDriver();
virtual asynStatus drvUserCreate(asynUser *pasynUser, const char *drvInfo, const char **pptypeName, size_t *psize) override;
virtual asynStatus readInt32(asynUser *pasynUser, epicsInt32 *value) override;
virtual asynStatus writeInt32(asynUser *pasynUser, epicsInt32 value) override;
virtual asynStatus readInt64(asynUser *pasynUser, epicsInt64 *value) override;
virtual asynStatus writeInt64(asynUser *pasynUser, epicsInt64 value) override;
virtual asynStatus readFloat64(asynUser *pasynUser, epicsFloat64 *value) override;
virtual asynStatus writeFloat64(asynUser *pasynUser, epicsFloat64 value) override;
virtual asynStatus connect(asynUser *pasynUser) override;
virtual asynStatus disconnect(asynUser *pasynUser) override;
void init();
private:
// The AMS address of the computer running the Beckhoff PLC.
AmsNetId ams_address_net_id;
uint16_t ams_address_port;
// The IP address of the computer running the Beckhoff PLC.
std::string ip_address_string;
// The symbol names specified in the INP and OUT fields of all the records.
std::vector<std::string> name_vec;
std::map<std::string, ads_symbol_entry_type> name_to_ads_symbol_entry_map;
std::map<std::string, size_t> name_to_value_buffer_size_map;
std::map<std::string, SYMBOL_HANDLE_TYPE> name_to_handle_map;
std::mutex handle_and_value_buffer_vec_mutex;
std::vector<std::pair<SYMBOL_HANDLE_TYPE, std::vector<uint8_t>>> handle_and_value_buffer_vec;
std::mutex name_to_return_code_and_value_buffer_pair_map_mutex;
std::map<std::string, std::pair<RETURN_CODE_TYPE, std::vector<uint8_t>>> name_to_return_code_and_value_buffer_pair_map;
};
#endif
#include "adsAsynPortDriver.hpp"
#include <string.h>
#include "alarm.h"
#include "cvtTable.h"
#include "dbDefs.h"
#include "dbAccess.h"
#include "initHooks.h"
#include "recGbl.h"
#include "recSup.h"
#include "devSup.h"
#include "link.h"
#include "epicsExport.h"
#include "epicsExit.h"
#include "iocsh.h"
#include <ads-utils/ads.hpp>
#include <ads-utils/error_codes.hpp>
#include <ads-utils/symbol_table_change.hpp>
#include <ads-utils/ads_state_change.hpp>
#include <ads-utils/ads_symbol_upload_info_2.hpp>
#include <ads-utils/ads_symbol_entry.hpp>
#include <ads-utils/ads_data_type_entry.hpp>
#include <ads-utils/create_handles.hpp>
#include <ads-utils/release_handles.hpp>
#include <ads-utils/read.hpp>
#include <ads-utils/write.hpp>
// 500 is recommended by the documentation
// https://infosys.beckhoff.com/english.php?content=../content/1033/tc3_adssamples_c/html/tcadsdll_api_cpp_sample17.htm&id=7873012517228788493
#define PARTITION_SIZE 500
adsAsynPortDriver::adsAsynPortDriver(std::string portName, AmsNetId ams_address_net_id, uint16_t ams_address_port, std::string ip_address_string) :
asynPortDriver(
portName.c_str(),
1,
asynInt32Mask | asynInt64Mask | asynFloat64Mask | asynDrvUserMask,
asynInt32Mask | asynInt64Mask | asynFloat64Mask,
0,
1, 0, 0)
{
printf("adsAsynPortDriver\n");
}
asynStatus adsAsynPortDriver::drvUserCreate(asynUser *pasynUser, const char *drvInfo, const char **pptypeName, size_t *psize) {
printf("drvUserCreate\n");
if (pasynUser && drvInfo) {
this->name_vec.push_back(std::string(drvInfo));
pasynUser->userData = (void*) drvInfo;
return asynSuccess;
}
else {
return asynError;
}
}
asynStatus adsAsynPortDriver::connect(asynUser *pasynUser) {
asynStatus status = asynPortDriver::connect(pasynUser);
printf("connect\n");
return status;
}
asynStatus adsAsynPortDriver::disconnect(asynUser *pasynUser) {
asynStatus status = asynPortDriver::disconnect(pasynUser);
printf("disconnect\n");
return status;
}
void adsAsynPortDriver::init() {
const AmsAddr ams_address = { ams_address_net_id, ams_address_port };
// ADS add route
try {
printf("AdsAddRoute\n");
long error_code = AdsAddRoute(ams_address_net_id, ip_address_string.c_str());
if (error_code) {
try {
printf("Error: AdsAddRoute: %s\n", general_ads_error_codes.at(error_code).c_str());
}
catch (const std::out_of_range& e) {
printf("Unrecognized error code: %ld.\n", error_code);
}
exit(EXIT_FAILURE);
}
}
catch (const std::exception &e) {
exit(EXIT_FAILURE);
}
// ADS port open
// Establishes a connection (communication port) to the TwinCAT message router.
printf("AdsPortOpenEx\n");
long port = AdsPortOpenEx();
if (!port) {
printf("Error: AdsPortOpenEx.\n");
AdsDelRoute(ams_address_net_id);
exit(EXIT_FAILURE);
}
// symbol table change notifications
try {
printf("register_symbol_table_change_callback_function\n");
register_symbol_table_change_callback_function(port, ams_address);
}
catch (const std::exception& e) {
AdsDelRoute(ams_address_net_id);
exit(EXIT_FAILURE);
}
// ADS state change notifications
try {
printf("register_ads_state_change_callback_function\n");
register_ads_state_change_callback_function(port, ams_address);
}
catch (const std::exception& e) {
AdsDelRoute(ams_address_net_id);
exit(EXIT_FAILURE);
}
// symbol upload information
struct ads_symbol_upload_info_2_type ads_symbol_upload_info_2;
try {
printf("get_ads_symbol_upload_info_2\n");
ads_symbol_upload_info_2 = get_ads_symbol_upload_info_2(port, ams_address);
}
catch (const std::exception& e) {
AdsDelRoute(ams_address_net_id);
exit(EXIT_FAILURE);
}
// symbol entry vector
std::vector<ads_symbol_entry_type> ads_symbol_entry_vec;
try {
printf("get_ads_symbol_entry_vec_one_by_one\n");
ads_symbol_entry_vec = get_ads_symbol_entry_vec_one_by_one(port, ams_address, name_vec);
}
catch (const std::exception& e) {
AdsDelRoute(ams_address_net_id);
exit(EXIT_FAILURE);
}
// name to ads symbol entry map
for (const auto& ads_symbol_entry : ads_symbol_entry_vec) {
name_to_ads_symbol_entry_map.insert({ads_symbol_entry.name_string, ads_symbol_entry});
}
// name to value buffer size map
for (const auto& ads_symbol_entry : ads_symbol_entry_vec) {
name_to_value_buffer_size_map.insert({ads_symbol_entry.name_string, ads_symbol_entry.header.size});
}
// name to handle map
name_to_handle_map = get_name_to_handle_map_split(port, ams_address, name_vec, PARTITION_SIZE);
for (const auto& name : name_vec) {
name_to_return_code_and_value_buffer_pair_map[name] = {};
}
}
asynStatus adsAsynPortDriver::readInt32(asynUser *pasynUser, epicsInt32 *value) {
//printf("readInt32\n");
if (pasynUser && pasynUser->userData) {
//printf("Reading parameter %s\n", (char*) pasynUser->userData);
}
else {
printf("Reading parameter (no userData available)\n");
}
return asynSuccess;
}
asynStatus adsAsynPortDriver::writeInt32(asynUser *pasynUser, epicsInt32 value) { return asynSuccess; }
asynStatus adsAsynPortDriver::readInt64(asynUser *pasynUser, epicsInt64 *value) { return asynSuccess; }
asynStatus adsAsynPortDriver::writeInt64(asynUser *pasynUser, epicsInt64 value) { return asynSuccess; }
asynStatus adsAsynPortDriver::readFloat64(asynUser *pasynUser, epicsFloat64 *value) { return asynSuccess; }
asynStatus adsAsynPortDriver::writeFloat64(asynUser *pasynUser, epicsFloat64 value) { return asynSuccess; }
// ----------------------------------------------------------------------------
static void devADSConfigureCallFunc(const iocshArgBuf *args) {
std::string asyn_port_name_string = std::string(args[0].sval);
printf("Asyn Port Name: %s\n", asyn_port_name_string.c_str());
// The AMS address of the computer running the Beckhoff PLC.
std::string ams_address_net_id_string = std::string(args[1].sval);
AmsNetId ams_address_net_id = AmsNetId(ams_address_net_id_string);
printf("NetID: %s\n", ams_address_net_id_string.c_str());
uint16_t ams_address_port = (uint16_t) args[2].ival;
printf("Port: %u\n", ams_address_port);
// The IP address of the computer running the Beckhoff PLC.
std::string ip_address_string = std::string(args[3].sval);
printf("IP Address: %s\n", ip_address_string.c_str());
new adsAsynPortDriver(asyn_port_name_string, ams_address_net_id, ams_address_port, ip_address_string);
}
static const iocshArg Arg_0 = { "Asyn_Port_Name", iocshArgString };
static const iocshArg Arg_1 = { "NetID", iocshArgString };
static const iocshArg Arg_2 = { "Port", iocshArgInt };
static const iocshArg Arg_3 = { "IP_Address", iocshArgString };
static const iocshArg *const Arg_List[] = { &Arg_0, &Arg_1, &Arg_2, &Arg_3 };
static const iocshFuncDef devADSConfigureFuncDef = { "devADS_Configure", 4, Arg_List, "asyn port name, AMS address, port, IP address" };
// ----------------------------------------------------------------------------
static void asyn_devADSRegistrar(void) {
iocshRegister(&devADSConfigureFuncDef, devADSConfigureCallFunc);
}
extern "C" { epicsExportRegistrar(asyn_devADSRegistrar); }
// ----------------------------------------------------------------------------
- Replies:
- asynPortDriver derived class connect() method not being called when expected Mark Rivers via Tech-talk
- References:
- synchronizing the value of a read/write record Thomas, Patrick via Tech-talk
- Re: synchronizing the value of a read/write record Mark Rivers via Tech-talk
- Re: synchronizing the value of a read/write record Thomas, Patrick via Tech-talk
- Re: synchronizing the value of a read/write record Thomas, Patrick via Tech-talk
- Re: synchronizing the value of a read/write record Michael Davidsaver via Tech-talk
- Re: synchronizing the value of a read/write record Thomas, Patrick via Tech-talk
- Navigate by Date:
- Prev:
Re: synchronizing the value of a read/write record Thomas, Patrick via Tech-talk
- Next:
asynPortDriver derived class connect() method not being called when expected Mark Rivers via Tech-talk
- Index:
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
<2025>
2026
- Navigate by Thread:
- Prev:
Re: synchronizing the value of a read/write record Thomas, Patrick via Tech-talk
- Next:
asynPortDriver derived class connect() method not being called when expected Mark Rivers via Tech-talk
- Index:
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
<2025>
2026
|