reorganises and cleans up some parts of the code

This commit is contained in:
2025-10-31 10:16:50 +01:00
parent 750436732c
commit d7bf3977fc
4 changed files with 170 additions and 176 deletions

View File

@@ -11,20 +11,46 @@
#include "asynStreamGeneratorDriver.h"
#include <epicsExport.h>
/* Wrapper to set config values and error out if needed.
/*******************************************************************************
* Kafka Methods
*/
static void set_config(rd_kafka_conf_t *conf, char *key, char *value) {
static void set_kafka_config_key(rd_kafka_conf_t *conf, char *key,
char *value) {
char errstr[512];
rd_kafka_conf_res_t res;
res = rd_kafka_conf_set(conf, key, value, errstr, sizeof(errstr));
if (res != RD_KAFKA_CONF_OK) {
// TODO
// g_error("Unable to set config: %s", errstr);
epicsStdoutPrintf("Failed to set config value %s : %s\n", key, value);
exit(1);
}
}
static rd_kafka_t *create_kafka_producer() {
char errstr[512];
rd_kafka_t *producer;
// Prepare configuration object
rd_kafka_conf_t *conf = rd_kafka_conf_new();
set_kafka_config_key(conf, "bootstrap.servers", "linkafka01:9092");
set_kafka_config_key(conf, "queue.buffering.max.messages", "1e7");
// Create the Producer
producer = rd_kafka_new(RD_KAFKA_PRODUCER, conf, errstr, sizeof(errstr));
if (!producer) {
epicsStdoutPrintf("Failed to create Kafka Producer: %s\n", errstr);
exit(1);
}
return producer;
}
/*******************************************************************************
* Static Methods Passed to Epics Threads that should run in the background
*/
static void udpPollerTask(void *drvPvt) {
asynStreamGeneratorDriver *pSGD = (asynStreamGeneratorDriver *)drvPvt;
pSGD->receiveUDP();
@@ -40,54 +66,13 @@ static void detectorProducerTask(void *drvPvt) {
pSGD->produceDetector();
}
// UDP Packet Definitions
struct __attribute__((__packed__)) UDPHeader {
uint16_t BufferLength;
uint16_t BufferType;
uint16_t HeaderLength;
uint16_t BufferNumber;
uint16_t RunCmdID;
uint16_t Status : 8;
uint16_t McpdID : 8;
uint16_t TimeStamp[3];
uint16_t Parameter0[3];
uint16_t Parameter1[3];
uint16_t Parameter2[3];
uint16_t Parameter3[3];
inline uint64_t nanosecs() {
uint64_t nsec{((uint64_t)TimeStamp[2]) << 32 |
((uint64_t)TimeStamp[1]) << 16 | (uint64_t)TimeStamp[0]};
return nsec * 100;
}
};
struct __attribute__((__packed__)) DetectorEvent {
uint64_t TimeStamp : 19;
uint16_t XPosition : 10;
uint16_t YPosition : 10;
uint16_t Amplitude : 8;
uint16_t Id : 1;
inline uint32_t nanosecs() { return TimeStamp * 100; }
};
struct __attribute__((__packed__)) MonitorEvent {
uint64_t TimeStamp : 19;
uint64_t Data : 21;
uint64_t DataID : 4;
uint64_t TriggerID : 3;
uint64_t Id : 1;
inline uint32_t nanosecs() { return TimeStamp * 100; }
};
/** Constructor for the asynStreamGeneratorDriver class.
* Calls constructor for the asynPortDriver base class.
* \param[in] portName The name of the asyn port driver to be created. */
/*******************************************************************************
* Stream Generator Methods
*/
asynStreamGeneratorDriver::asynStreamGeneratorDriver(const char *portName,
const char *ipPortName,
const int numChannels)
: asynPortDriver(portName, 1, /* maxAddr */
// 5,
asynInt32Mask | asynInt64Mask |
asynDrvUserMask, /* Interface mask */
asynInt32Mask | asynInt64Mask, /* Interrupt mask */
@@ -100,48 +85,23 @@ asynStreamGeneratorDriver::asynStreamGeneratorDriver(const char *portName,
0), /* Default stack size*/
num_channels(numChannels + 1), monitorQueue(1000, false),
detectorQueue(1000, false) {
const char *functionName = "asynStreamGeneratorDriver";
// Parameter Setup
char pv_name_buffer[100];
P_Counts = new int[this->num_channels];
asynStatus status;
// Create PVs templated on Channel Number
for (size_t i = 0; i < this->num_channels; ++i) {
memset(pv_name_buffer, 0, 100);
epicsSnprintf(pv_name_buffer, 100, P_CountsString, i);
status = createParam(pv_name_buffer, asynParamInt32, P_Counts + i);
setIntegerParam(P_Counts[i], 0);
printf("%s %d %d %d\n", pv_name_buffer, P_Counts[i], i, status);
}
char errstr[512];
// Create client configuration
rd_kafka_conf_t *conf = rd_kafka_conf_new();
set_config(conf, "bootstrap.servers", "linkafka01:9092");
set_config(conf, "queue.buffering.max.messages", "1e7");
// Create the Monitor Producer instance.
this->monitorProducer =
rd_kafka_new(RD_KAFKA_PRODUCER, conf, errstr, sizeof(errstr));
if (!this->monitorProducer) {
// TODO
// g_error("Failed to create new producer: %s", errstr);
exit(1);
}
conf = rd_kafka_conf_new();
set_config(conf, "bootstrap.servers", "linkafka01:9092");
set_config(conf, "queue.buffering.max.messages", "1e7");
// Create the Detector Producer instance.
this->detectorProducer =
rd_kafka_new(RD_KAFKA_PRODUCER, conf, errstr, sizeof(errstr));
if (!this->detectorProducer) {
// TODO
// g_error("Failed to create new producer: %s", errstr);
exit(1);
}
this->monitorProducer = create_kafka_producer();
this->detectorProducer = create_kafka_producer();
// Setup for Thread Producing Monitor Kafka Events
status =
@@ -150,11 +110,9 @@ asynStreamGeneratorDriver::asynStreamGeneratorDriver(const char *portName,
epicsThreadGetStackSize(epicsThreadStackMedium),
(EPICSTHREADFUNC)::monitorProducerTask, this) == NULL);
if (status) {
// printf("%s:%s: epicsThreadCreate failure, status=%d\n", driverName,
// functionName, status);
printf("%s:%s: epicsThreadCreate failure, status=%d\n",
"StreamGenerator", "init", status);
return;
printf("%s:%s: epicsThreadCreate failure, status=%d\n", driverName,
functionName, status);
exit(1);
}
// Setup for Thread Producing Detector Kafka Events
@@ -164,15 +122,19 @@ asynStreamGeneratorDriver::asynStreamGeneratorDriver(const char *portName,
(EPICSTHREADFUNC)::detectorProducerTask,
this) == NULL);
if (status) {
// printf("%s:%s: epicsThreadCreate failure, status=%d\n", driverName,
// functionName, status);
printf("%s:%s: epicsThreadCreate failure, status=%d\n",
"StreamGenerator", "init", status);
return;
printf("%s:%s: epicsThreadCreate failure, status=%d\n", driverName,
functionName, status);
exit(1);
}
// UDP Receive Setup
pasynOctetSyncIO->connect(ipPortName, 0, &pasynUDPUser, NULL);
status = pasynOctetSyncIO->connect(ipPortName, 0, &pasynUDPUser, NULL);
if (status) {
printf("%s:%s: Couldn't open connection %s, status=%d\n", driverName,
functionName, ipPortName, status);
exit(1);
}
/* Create the thread that receives UDP traffic in the background */
status = (asynStatus)(epicsThreadCreate(
@@ -180,12 +142,16 @@ asynStreamGeneratorDriver::asynStreamGeneratorDriver(const char *portName,
epicsThreadGetStackSize(epicsThreadStackMedium),
(EPICSTHREADFUNC)::udpPollerTask, this) == NULL);
if (status) {
// printf("%s:%s: epicsThreadCreate failure, status=%d\n", driverName,
// functionName, status);
printf("%s:%s: epicsThreadCreate failure, status=%d\n",
"StreamGenerator", "init", status);
return;
printf("%s:%s: epicsThreadCreate failure, status=%d\n", driverName,
functionName, status);
exit(1);
}
}
asynStreamGeneratorDriver::~asynStreamGeneratorDriver() {
// should make sure queues are empty and freed
// and that the kafka producers are flushed and freed
delete[] P_Counts;
// TODO add exit should perhaps ensure the queue is flushed
// rd_kafka_poll(producer, 0);
@@ -194,50 +160,12 @@ asynStreamGeneratorDriver::asynStreamGeneratorDriver(const char *portName,
// epicsStdoutPrintf("Kafka Queue Size %d\n", rd_kafka_outq_len(producer));
}
asynStreamGeneratorDriver::~asynStreamGeneratorDriver() {
// should make sure queues are empty and freed
// and that the kafka producers are flushed and freed
delete[] P_Counts;
}
// // TODO pretty sure I don't actually need to overwrite this
// asynStatus asynStreamGeneratorDriver::readInt32(asynUser *pasynUser,
// epicsInt32 *value) {
// // asynStatus asynStreamGeneratorDriver::readInt64(asynUser *pasynUser,
// // epicsInt64 *value) {
//
// const char *paramName;
// int function = pasynUser->reason;
// asynStatus status;
//
// // TODO not freed
// getParamName(function, &paramName);
//
// bool is_p_counts = false;
// for (size_t i = 0; i < num_channels; ++i) {
// is_p_counts = is_p_counts | function == P_Counts[i];
// }
//
// if (is_p_counts) {
// status = getIntegerParam(function, value);
//
// asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s: function %d %s
// %d\n",
// "StreamGenerator", "readInt64", function, paramName,
// status);
// // return status;
// return asynSuccess;
// } else {
// return asynError;
// }
// return asynSuccess;
// }
void asynStreamGeneratorDriver::receiveUDP() {
asynStatus status;
int isConnected;
char buffer[1500];
const size_t buffer_size = 1500;
char buffer[buffer_size];
size_t received;
int eomReason;
@@ -256,22 +184,21 @@ void asynStreamGeneratorDriver::receiveUDP() {
counts[i] = 0;
}
// epicsStdoutPrintf("polling!!");
status = pasynManager->isConnected(pasynUDPUser, &isConnected);
if (status) {
asynPrint(pasynUserSelf, ASYN_TRACE_ERROR,
"%s:%s: error calling pasynManager->isConnected, "
"status=%d, error=%s\n",
"StreamGenerator", "receiveUDP", status,
driverName, "receiveUDP", status,
pasynUDPUser->errorMessage);
// driverName, functionName, status,
// pasynUserIPPort_->errorMessage);
}
asynPrint(pasynUserSelf, ASYN_TRACEIO_DRIVER,
"%s:%s: isConnected = %d\n", //
"StreamGenerator", "receiveUDP", isConnected);
driverName, "receiveUDP", isConnected);
status = pasynOctetSyncIO->read(pasynUDPUser, buffer, 1500,
status = pasynOctetSyncIO->read(pasynUDPUser, buffer, buffer_size,
0, // timeout
&received, &eomReason);
@@ -279,13 +206,19 @@ void asynStreamGeneratorDriver::receiveUDP() {
// asynPrint(
// pasynUserSelf, ASYN_TRACE_ERROR,
// "%s:%s: error calling pasynOctetSyncIO->read, status=%d\n",
// "StreamGenerator", "receiveUDP", status);
// driverName, "receiveUDP", status);
// buffer[received] = 0;
if (received) {
asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s: received %d\n",
"StreamGenerator", "receiveUDP", received);
// asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s: received %f %d
// received\n",
// driverName, "receiveUDP", (double) received /
// 1500., received);
// asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s: received
// %d\n",
// driverName, "receiveUDP", received);
UDPHeader *header = (UDPHeader *)buffer;
@@ -293,11 +226,13 @@ void asynStreamGeneratorDriver::receiveUDP() {
// TODO lots of checks and validation missing everywhere here
if (received == total_events * 6 + 42) {
asynPrint(pasynUserSelf, ASYN_TRACE_ERROR,
"%s:%s: received packet %d with %d events (%" PRIu64
")\n",
"StreamGenerator", "receiveUDP", header->BufferNumber,
total_events, header->nanosecs());
// asynPrint(pasynUserSelf, ASYN_TRACE_ERROR,
// "%s:%s: received packet %d with %d events (%"
// PRIu64
// ")\n",
// driverName, "receiveUDP",
// header->BufferNumber, total_events,
// header->nanosecs());
for (size_t i = 0; i < total_events; ++i) {
char *event = (buffer + 21 * 2 + i * 6);
@@ -308,7 +243,7 @@ void asynStreamGeneratorDriver::receiveUDP() {
// asynPrint(
// pasynUserSelf, ASYN_TRACE_ERROR,
// "%s:%s: event (%03d) on monitor %d (%" PRIu64
// ")\n", "StreamGenerator", "receiveUDP", i,
// ")\n", driverName, "receiveUDP", i,
// m_event->DataID, header->nanosecs() +
// (uint64_t)m_event->nanosecs());
@@ -342,11 +277,11 @@ void asynStreamGeneratorDriver::receiveUDP() {
counts[i] += val;
}
asynPrint(pasynUserSelf, ASYN_TRACE_ERROR,
"%s:%s: det: (%d), mon0: (%d), mon1: (%d), mon2: "
"(%d), mon3: (%d)\n",
"StreamGenerator", "receiveUDP", counts[0], counts[1],
counts[2], counts[3], counts[4]);
// asynPrint(pasynUserSelf, ASYN_TRACE_ERROR,
// "%s:%s: det: (%d), mon0: (%d), mon1: (%d), mon2: "
// "(%d), mon3: (%d)\n",
// driverName, "receiveUDP", counts[0],
// counts[1], counts[2], counts[3], counts[4]);
lock();
for (size_t i = 0; i < num_channels; ++i) {
@@ -356,12 +291,12 @@ void asynStreamGeneratorDriver::receiveUDP() {
unlock();
} else {
asynPrint(pasynUserSelf, ASYN_TRACE_ERROR,
"%s:%s: invalid UDP packet\n", "StreamGenerator",
"%s:%s: invalid UDP packet\n", driverName,
"receiveUDP");
}
}
epicsThreadSleep(1); // seconds
// epicsThreadSleep(1); // seconds
}
}
@@ -394,6 +329,8 @@ void asynStreamGeneratorDriver::produceMonitor() {
epicsThreadSleep(0.001); // seconds
}
// TODO can probably just replace the current
// instead of always getting new object
epicsTimeStamp now = epicsTime::getCurrent();
// At least every 0.2 seconds
@@ -407,7 +344,10 @@ void asynStreamGeneratorDriver::produceMonitor() {
builder.Clear();
auto message = CreateEventMessageDirect(
builder, "monitor", message_id++, 0, &tof, &did);
builder, "monitor", message_id++,
((uint64_t)now.secPastEpoch) * 1'000'000'000ull +
((uint64_t)now.nsec),
&tof, &did);
builder.Finish(message, "ev42");
// printf("buffer size: %d\n", builder.GetSize());
@@ -432,9 +372,9 @@ void asynStreamGeneratorDriver::produceMonitor() {
rd_kafka_poll(monitorProducer, 0);
printf("Monitor Events Queued before sending %d\n",
this->monitorQueue.getHighWaterMark());
this->monitorQueue.resetHighWaterMark();
// printf("Monitor Events Queued before sending %d\n",
// this->monitorQueue.getHighWaterMark());
// this->monitorQueue.resetHighWaterMark();
tof.clear();
did.clear();
@@ -484,7 +424,10 @@ void asynStreamGeneratorDriver::produceDetector() {
builder.Clear();
auto message = CreateEventMessageDirect(
builder, "detector", message_id++, 0, &tof, &did);
builder, "detector", message_id++,
((uint64_t)now.secPastEpoch) * 1'000'000'000ull +
((uint64_t)now.nsec),
&tof, &did);
builder.Finish(message, "ev42");
// printf("buffer size: %d\n", builder.GetSize());
@@ -509,9 +452,9 @@ void asynStreamGeneratorDriver::produceDetector() {
rd_kafka_poll(detectorProducer, 0);
printf("Detector Events Queued before sending %d\n",
this->detectorQueue.getHighWaterMark());
this->detectorQueue.resetHighWaterMark();
// printf("Detector Events Queued before sending %d\n",
// this->detectorQueue.getHighWaterMark());
// this->detectorQueue.resetHighWaterMark();
tof.clear();
did.clear();
@@ -520,13 +463,11 @@ void asynStreamGeneratorDriver::produceDetector() {
}
}
/* Configuration routine. Called directly, or from the iocsh function below */
/*******************************************************************************
* Methods exposed to IOC Shell
*/
extern "C" {
/** EPICS iocsh callable function to call constructor for the
* asynStreamGeneratorDriver class. \param[in] portName The name of the asyn
* port driver to be created. */
asynStatus asynStreamGeneratorDriverConfigure(const char *portName,
const char *ipPortName,
const int numChannels) {
@@ -534,8 +475,6 @@ asynStatus asynStreamGeneratorDriverConfigure(const char *portName,
return asynSuccess;
}
/* EPICS iocsh shell commands */
static const iocshArg initArg0 = {"portName", iocshArgString};
static const iocshArg initArg1 = {"ipPortName", iocshArgString};
static const iocshArg initArg2 = {"numChannels", iocshArgInt};