#ifndef CA_WRAPPER_H #define CA_WRAPPER_H #include #include #include /* * Go-exported callback declarations. * These are implemented in ca_callback.go (via //export directives). * * goCAMonitorCallback is called for each value update received from CA. * goCAConnectionCallback is called when a channel connects or disconnects. */ extern void goCAMonitorCallback(uintptr_t handle, int dbrType, long count, void *dbr, int severity, double epicsTimeSecs); extern void goCAConnectionCallback(uintptr_t handle, int connected); /* * CA monitor callback shim. * * CA calls this function with the fixed evargs signature. We extract the * relevant fields and forward them to the Go callback. */ static void caMonitorCallbackShim(struct event_handler_args args) { if (args.status != ECA_NORMAL) { return; } double timeSecs = 0.0; int severity = 0; /* * Determine the timestamp and alarm severity from the DBR type. * We request DBR_TIME_* types so the timestamp is embedded in the value * buffer right after the alarm fields (struct dbr_time_double et al.). */ switch (args.type) { case DBR_TIME_DOUBLE: { const struct dbr_time_double *p = (const struct dbr_time_double *)args.dbr; timeSecs = (double)p->stamp.secPastEpoch + (double)p->stamp.nsec / 1e9; severity = (int)p->severity; break; } case DBR_TIME_FLOAT: { const struct dbr_time_float *p = (const struct dbr_time_float *)args.dbr; timeSecs = (double)p->stamp.secPastEpoch + (double)p->stamp.nsec / 1e9; severity = (int)p->severity; break; } case DBR_TIME_LONG: { const struct dbr_time_long *p = (const struct dbr_time_long *)args.dbr; timeSecs = (double)p->stamp.secPastEpoch + (double)p->stamp.nsec / 1e9; severity = (int)p->severity; break; } case DBR_TIME_SHORT: { const struct dbr_time_short *p = (const struct dbr_time_short *)args.dbr; timeSecs = (double)p->stamp.secPastEpoch + (double)p->stamp.nsec / 1e9; severity = (int)p->severity; break; } case DBR_TIME_STRING: { const struct dbr_time_string *p = (const struct dbr_time_string *)args.dbr; timeSecs = (double)p->stamp.secPastEpoch + (double)p->stamp.nsec / 1e9; severity = (int)p->severity; break; } case DBR_TIME_ENUM: { const struct dbr_time_enum *p = (const struct dbr_time_enum *)args.dbr; timeSecs = (double)p->stamp.secPastEpoch + (double)p->stamp.nsec / 1e9; severity = (int)p->severity; break; } default: break; } goCAMonitorCallback((uintptr_t)args.usr, (int)args.type, (long)args.count, (void *)args.dbr, severity, timeSecs); } /* * CA connection callback shim. * * CA calls this with a connection_handler_args struct. We map the op field * to a simple boolean (1 = connected, 0 = disconnected) and forward. */ static void caConnectionCallbackShim(struct connection_handler_args args) { int connected = (args.op == CA_OP_CONN_UP) ? 1 : 0; goCAConnectionCallback((uintptr_t)ca_puser(args.chid), connected); } /* * Thin helpers so CGo callers do not need to deal with C function-pointer * syntax directly. All CA API calls stay in C to avoid issues with CGo * passing Go pointers across the CA callback boundary. */ /* Create a channel with the connection shim as the connection callback. * user_handle is the Go uintptr_t that identifies this channel in the handle * table. Returns the ECA_* status code. */ static int caCreateChannel(const char *pvName, uintptr_t userHandle, chid *pChid) { return ca_create_channel(pvName, caConnectionCallbackShim, (void *)userHandle, CA_PRIORITY_DEFAULT, pChid); } /* Add a value-monitor event on chid. Returns the ECA_* status code and stores * the evid in *pEvid. */ static int caAddMonitor(chid ch, short dbrType, uintptr_t userHandle, evid *pEvid) { return ca_add_event(dbrType, ch, caMonitorCallbackShim, (void *)userHandle, pEvid); } /* Wrappers for ca_get / ca_put, which are macros in cadef.h that expand to * ca_array_get / ca_array_put with count=1. CGo cannot call macros directly, * so we provide thin static-inline shims here. */ static int caGet(chtype type, chid chan, void *pValue) { return ca_array_get(type, 1u, chan, pValue); } static int caPut(chtype type, chid chan, const void *pValue) { return ca_array_put(type, 1u, chan, pValue); } /* Retrieve the current thread's CA context as an opaque pointer. * Store the result and pass it to caAttachContext() from other threads. */ static void *caCurrentContext(void) { return (void *)ca_current_context(); } /* Attach an existing CA context to the calling thread. Must be called at the * start of every goroutine (OS thread) that makes CA calls, if that thread did * not call ca_context_create() itself. Returns ECA_NORMAL on success. */ static int caAttachContext(void *ctx) { return ca_attach_context((struct ca_client_context *)ctx); } #endif /* CA_WRAPPER_H */