ACQ400_XRM (xrmIoc)
Loading...
Searching...
No Matches
xrm_structs.h
Go to the documentation of this file.
1/** @file xrm_structs.h
2 * @brief Declarations for key XRM structures for internal and wire (external) use.
3 * - @ref FMT
4 * - @ref SOE_LUT
5 * - @ref SOE_HOLD_TABLE
6 * xrm_structs.h
7 *
8 * Created on: 6 Feb 2026
9 * Author: pgm
10 */
11
12#include <epicsTypes.h>
13
14#ifndef XRMIOCAPP_SRC_XRM_STRUCTS_H_
15#define XRMIOCAPP_SRC_XRM_STRUCTS_H_
16
17#include <cstring> // memset()
18
19/** @brief FMT_ROW defines a row of FMT: */
20struct FMT_ROW {
21 epicsUInt16 event; /**< FNAL Event number */
22 epicsUInt16 pad; /**< 32 bit alignment is best, available for future */
23 epicsUInt32 client_data; /**< opaque value to pass back */
24 epicsInt64 timestamp; /**< 64 bit WR timestamp in usec from EPOCH */
25};
26
27/* ** we're losing one bit here, but it's OK, we have time..
28 * >>> usec_per_year=365*24*3600*1000
29 * >>> max_int64 = 2**63
30 * max_int64//usec_per_year
31292471208
32 * this is probably why CERN has integer nsec :-)
33 */
34const epicsUInt16 EV99 = 65535U; // denotes last event in table.
35
36/** define number of rows in FMT. */
37const int FMT_ROWS = 64;
38//#define FMT_ROWS 64
39
40/** @brief FMT : FNAL Multicast Table
41
42 * input from plant: 20Hz
43
44 * This is the binary implementation that goes out on the wire.
45
46 * each row of the FMT is a @ref struct FMT_ROW.
47
48 ROW | event | pad | client_data | timestamp
49------|-------|-----|-------------|----------
50 0 | u16 | u16 | u32 | int64
51 1 | u16 | u16 | u32 | int64
52 2 | u16 | u16 | u32 | int64
53 .. | ... | ... | ... | ...
54 64 | u16 | u16 | u32 | int64
55
56 */
57typedef struct FMT_ROW FMT[FMT_ROWS];
58
59static inline void clean(FMT fmt) {
60 memset(fmt, 0, sizeof(FMT));
61}
62
63/** @brief SOE_LUT_ROW defines a row of SOE_LUT. */
65 epicsUInt16 event; /**< FNAL Event number. */
66 epicsUInt16 pad; /**< 32 bit alignment is best, available for future. */
67 epicsUInt32 pv_id; /**< PV id match event to this PV */
68 epicsInt32 offset_us; /**< time offset Before or After Event, to the limit of data this cycle */
69};
70
71const int SOE_LUT_ROWS = 64;
72
73/** @brief SOE_LUT Sample On Event Lookup Table definition.
74 *
75 * matches events to PV's with a selectable time offset.
76
77 * input from user at user-timescale.
78
79 * in EPICS, we provide a "ROW_EDIT" PV to enable update, this works well for Phoebus and P4P
80
81 * @@todo: unsure how to make a direct pvput for the whole table.
82 - Currently this would have to be an array of u32
83 - Don't know how to make a EPICS V4 array for this
84 - Workaround: the "ROW EDIT" is available to change a row at a time, or a set of rows.
85
86 ROW | event | pad | pv_id | offset_us
87------|-------|-----|-------|----------
88 0 | u16 | u16 | u32 | int32
89 1 | u16 | u16 | u32 | int32
90 2 | u16 | u16 | u32 | int32
91 .. | ... | ... | ... | ...
92 64 | u16 | u16 | u32 | int32
93
94 */
95typedef struct SOE_LUT_ROW SOE_LUT[SOE_LUT_ROWS];
96
97/** @brief SOE_HOLD_HEADER Header for Hold Table.
98 * lut_row is a copy of the relevant SOE_LUT_ROW, for traceability
99 * + we embed a version id 'S'<<8 | VERSION to detect current and future updates.
100 *
101 */
102
104 SOE_LUT_ROW lut_row; /**< LUT entry that triggered this HOLD */
105 epicsUInt32 client_data; /**< copied from FMT (if required) @todo more required? */
106
107 epicsInt64 timestamp; /**< cross check: which FMT update this derives from. */
108 epicsUInt16 data_offset; /**< offset of RAW DATA in u32 from start of table. */
109 /* description of raw sample from hardware
110 * it's not totally raw because all AI are presented as calibrated V.
111 * but after that a series of U32 representing DI, SPAD
112 * this is not in the spec, but will be useful for validation.
113 */
114 epicsUInt8 ss_u32; /**< sample size (u32) */
115 epicsUInt8 ai_count; /**< number of AI (floats) in data */
116 epicsUInt8 di_count; /**< number of DI (u32) in data */
117 epicsUInt8 sp_count; /**< number of SP (u32) in data Scratch Pad (meta-data) */
118};
119
120#define SOE_HOLD_HEADER_VERSION 1
121
122static inline int setVersion(SOE_HOLD_HEADER& soe)
123{
124 soe.lut_row.pad = ('S'<<8) | SOE_HOLD_HEADER_VERSION;
125 return SOE_HOLD_HEADER_VERSION;
126}
127
128static inline int getVersion(SOE_HOLD_HEADER& soe)
129{
130 if (soe.lut_row.pad>>8 == 'S'){
131 return soe.lut_row.pad&0x0ff;
132 }
133 return 0;
134}
135
136
137const int SPAD0_SC = 0; /**< SPAD[0] is sample count (u32) */
138const int SPAD1_TS = 1; /**< SPAD[1] is WR TS 3 bit seconds, 28 bit ticks */
139
140const int SOE_HLD_ROWS = 64;
141
142typedef epicsUInt32 U32;
143
144/** @brief SOE_HOLD_TABLE
145 - This is the OUTPUT from each CYCLE
146
147 - For N events, the OUTPUT comprises:
148
149 1. N+1 SOE_HOLD_HEADER rows, headers for N events + 1 row of zeros (DELIMITER)
150 2. N RAW SAMPLE rows
151
152 - The HEADER includes the geometry of the raw sample entries, so the data is self-describing.
153
154 - The RAW SAMPLE row varies per unit type, the fixed header includes info to access the RAW SAMPLE.
155
156 - We prefer to offer the RAW sample because
157
158 + Blitting off a row of data is our most efficient transfer
159 + No conversion to EGU's. User to do that thanks to $UUT:*:EOFF,ESLO
160 + RAW sample includes METADATA for checking purposes.
161
162 - To interpret a received HOLD DATA:
163
164 + iterate the SOE_HOLD_HEADER rows until zero
165
166 + use data_offset to access the data.
167
168 - the first 4 data elements shown are in fact a copy of the relevant SOE_LUT_ROW, where
169 + lut_row.pv_id, lut_row.event and lut_row.offset_us allow the user to check the row match with the original LUT definition.
170 + lut_row.pad has the table version encoding as
171 + 'S' << 16 | VERSION
172 + currently the VERSION is 1, but this should allow us to check on any future upgrade.
173
174 - In summary, the memory layout looks like this:
175
176 ROW | pv_id,event,pad,offset_us | client_data | timestamp | data_offset | ss_u32 | ai_count | di_count | sp_count
177------|---------------------------|-------------|-----------|-------------|--------|----------|----------|---------
178 0 | u32,u16,u16,int32 | u32 | int64 | u16 | u8 | u8 | u8 | u8
179 1 | u32,u16,u16,int32 | u32 | int64 | u16 | u8 | u8 | u8 | u8
180 ... | u32,u16,u16,int32 | u32 | int64 | u16 | u8 | u8 | u8 | u8
181 DEL | 0,0,0,0 | 0 | 0 | 0 | 0 | 0 | 0 | 0
182
183 RAW | ai | di | sp
184 -----|----|----|---
185 R0 | i16|u32 |U32
186 R1 | i16|u32 |U32
187
188
189```
190 Example 4 entries
191 sizeof(SOE_HOLD_HEADER==32)
192 XRMMAGPS: SSB=128 ruler in byte*2:
193 00000111112222233333444445555566666777778888899999AAAAABBBBBCCCC
194 0246802468024680246802468024680246802468024680246802468024680246
195 |SOE HOLD 1 |
196 |SOE HOLD 2 |
197 |SOE HOLD 3 |
198 |SOE HOLD 4 |
199 |0000000000000000|
200 |RAW SMPL 1 AIAIAIAIAIAIAIAIAIAIAIDIDISPSPSPSPSPSPSPSPSPSPSPSPSP|
201 |RAW SMPL 2 AIAIAIAIAIAIAIAIAIAIAIDIDISPSPSPSPSPSPSPSPSPSPSPSPSP|
202 |RAW SMPL 3 AIAIAIAIAIAIAIAIAIAIAIDIDISPSPSPSPSPSPSPSPSPSPSPSPSP|
203 |RAW SMPL 4 AIAIAIAIAIAIAIAIAIAIAIDIDISPSPSPSPSPSPSPSPSPSPSPSPSP|
204
205
206 showing the SOE HOLD struct on a line of bytes
207 0123456789012345678901246802468012468024680246802468024680246802468024680246
208 EV_______________________________| epicsUInt16 event;
209 __PA_____________________________| epicsUint16 pad; // aka VERSION
210_____PVID ________________________| epicsUInt32 pv_id;
211 ________OFFS_____________________| epicsInt32 offset_usec;
212 ____________CLID_________________| epicsUInt32 client_data;
213 ________________TIMSTAMP_________| epicsInt64 timestamp;
214 ________________________DO_______| epicsUInt16 data_offset;
215 __________________________SADP___| epicsUInt8 ss_u32, ai_count, di_count, sp_count;
216
217 ```
218
219 Or in terms of offsets (sizes in bytes):
220 ```
221 (base) pgm@hoy6:~/PROJECTS/ACQ400/ACQ400_XRM$ ./bin/linux-x86_64/xrm_ht_monitor -S
222structure_test
223sizeof SOE_HOLD_HEADER 32
224offsetof lut_row.event : 0
225offsetof lut_row.pad : 2
226offsetof lut_row.pv_id : 4
227offsetof lut_row.offset_us : 8
228offsetof client_data : 12
229offsetof timestamp : 16
230offsetof data_offset : 24
231offsetof ss_u32 : 26
232offsetof ai_count : 27
233offsetof di_count : 28
234offsetof sp_count : 29
235 ```
236
237 Actual wire protocol:
238 - We meet the letter of the requirement by sending as a PVA ARRAY of U32
239 - where NORD gives the overall size of the table, including DATA.
240
241 - In the above example, MAGPS with 4 EVENTS,
242 - NORD = (5 * sizeof(SOE_HOLD_HEADER))/sizeof(int32) + 4*SSB/sizeof(int32)
243 - NORD = (5 * 32)/4 + 128
244 - NORD = 168
245
246 @@todo We've attempted to meet the spirit of the requirement using the PVXS API
247 to create an Array of Groups, but this has not been a success.
248 Happy to revisit later when we have an example that works.
249 */
250typedef struct SOE_HOLD_HEADER* SOE_HOLD_TABLE; // many more than 1 of course..
251
252
253/* For data allocation, what is the MAXIMUM size of table:
254 * HOLD_MAXSIZE = 64*20+64*(128*2+16*4) = 21760B = 6400LW
255 * */
256
257static inline const int HOLD_DATA_OFF() { return (SOE_HLD_ROWS+1)*sizeof(SOE_HOLD_HEADER); };
258static inline const int HOLD_MAXSIZE(unsigned ssb) { return HOLD_DATA_OFF() + SOE_HLD_ROWS*ssb; }
259static inline const int HOLD_MAX_NELM(unsigned ssb) { return HOLD_MAXSIZE(ssb)/sizeof(long); }
260
261
262
263/* actual sample data:
264 */
265
266const int XRM_MAGPS_AI16 = 32;
267const int XRM_MAGPS_DI32 = 2;
268const int XRM_MAGPS_SP32 = 6; // pad to round number
269
271 epicsInt16 ai[XRM_MAGPS_AI16];
272 epicsUInt32 di[XRM_MAGPS_DI32];
273 epicsUInt32 sp[XRM_MAGPS_SP32];
274};
275
276
277const int XRM_QPMS_AI16 = 128;
278const int XRM_QPMS_DI32 = 0;
279const int XRM_QPMS_SP32 = 8;
280
282 epicsInt16 ai[XRM_MAGPS_AI16];
283 epicsUInt32 di[XRM_MAGPS_DI32];
284 epicsUInt32 sp[XRM_MAGPS_SP32];
285};
286
287const int XRM_INST_A_AI16 = 32;
288const int XRM_INST_A_DI32 = 0;
289const int XRM_INST_A_SP32 = 4;
290
292 epicsInt16 ai[XRM_INST_A_AI16];
293 epicsUInt32 di[XRM_INST_A_DI32];
294 epicsUInt32 sp[XRM_INST_A_SP32];
295};
296
297const int XRM_INST_B_AI16 = 32;
298const int XRM_INST_B_DI32 = 1;
299const int XRM_INST_B_SP32 = 3;
300
302 epicsInt16 ai[XRM_INST_A_AI16];
303 epicsUInt32 di[XRM_INST_A_DI32];
304 epicsUInt32 sp[XRM_INST_A_SP32];
305};
306
307
308void print(FMT& fmt, bool verbose = false);
309void print(SOE_LUT& lut, bool verbose = false);
310void print(SOE_HOLD_HEADER* ht, bool verbose = false);
311
312
313
314
315#endif /* XRMIOCAPP_SRC_XRM_STRUCTS_H_ */
FMT_ROW defines a row of FMT:
Definition xrm_structs.h:20
epicsUInt16 pad
Definition xrm_structs.h:22
epicsInt64 timestamp
Definition xrm_structs.h:24
epicsUInt32 client_data
Definition xrm_structs.h:23
epicsUInt16 event
Definition xrm_structs.h:21
SOE_HOLD_HEADER Header for Hold Table. lut_row is a copy of the relevant SOE_LUT_ROW,...
Definition xrm_structs.h:103
epicsUInt8 di_count
Definition xrm_structs.h:116
epicsUInt8 ai_count
Definition xrm_structs.h:115
epicsUInt16 data_offset
Definition xrm_structs.h:108
epicsInt64 timestamp
Definition xrm_structs.h:107
epicsUInt32 client_data
Definition xrm_structs.h:105
epicsUInt8 sp_count
Definition xrm_structs.h:117
epicsUInt8 ss_u32
Definition xrm_structs.h:114
SOE_LUT_ROW lut_row
Definition xrm_structs.h:104
SOE_LUT_ROW defines a row of SOE_LUT.
Definition xrm_structs.h:64
epicsUInt16 pad
Definition xrm_structs.h:66
epicsUInt16 event
Definition xrm_structs.h:65
epicsInt32 offset_us
Definition xrm_structs.h:68
epicsUInt32 pv_id
Definition xrm_structs.h:67
Definition xrm_structs.h:291
Definition xrm_structs.h:301
Definition xrm_structs.h:270
Definition xrm_structs.h:281
const int SPAD0_SC
Definition xrm_structs.h:137
struct SOE_LUT_ROW SOE_LUT[SOE_LUT_ROWS]
SOE_LUT Sample On Event Lookup Table definition.
Definition xrm_structs.h:95
struct SOE_HOLD_HEADER * SOE_HOLD_TABLE
SOE_HOLD_TABLE.
Definition xrm_structs.h:250
const int SPAD1_TS
Definition xrm_structs.h:138
struct FMT_ROW FMT[FMT_ROWS]
FMT : FNAL Multicast Table.
Definition xrm_structs.h:57
const int FMT_ROWS
Definition xrm_structs.h:37