8 #define DEFAULT_REFRESH_PERIOD_MS 100 |
8 #define DEFAULT_REFRESH_PERIOD_MS 100 |
9 #define HMI_BUFFER_SIZE %(buffer_size)d |
9 #define HMI_BUFFER_SIZE %(buffer_size)d |
10 #define HMI_ITEM_COUNT %(item_count)d |
10 #define HMI_ITEM_COUNT %(item_count)d |
11 #define HMI_HASH_SIZE 8 |
11 #define HMI_HASH_SIZE 8 |
12 #define MAX_CONNECTIONS %(max_connections)d |
12 #define MAX_CONNECTIONS %(max_connections)d |
|
13 #define MAX_CON_INDEX MAX_CONNECTIONS - 1 |
13 |
14 |
14 static uint8_t hmi_hash[HMI_HASH_SIZE] = {%(hmi_hash_ints)s}; |
15 static uint8_t hmi_hash[HMI_HASH_SIZE] = {%(hmi_hash_ints)s}; |
15 |
16 |
16 /* PLC reads from that buffer */ |
17 /* PLC reads from that buffer */ |
17 static char rbuf[HMI_BUFFER_SIZE]; |
18 static char rbuf[HMI_BUFFER_SIZE]; |
18 |
19 |
19 /* PLC writes to that buffer */ |
20 /* PLC writes to that buffer */ |
20 static char wbuf[HMI_BUFFER_SIZE]; |
21 static char wbuf[HMI_BUFFER_SIZE]; |
21 |
22 |
22 /* TODO change that in case of multiclient... */ |
|
23 /* worst biggest send buffer. FIXME : use dynamic alloc ? */ |
23 /* worst biggest send buffer. FIXME : use dynamic alloc ? */ |
24 static char sbuf[HMI_HASH_SIZE + HMI_BUFFER_SIZE + (HMI_ITEM_COUNT * sizeof(uint32_t))]; |
24 static char sbuf[HMI_HASH_SIZE + HMI_BUFFER_SIZE + (HMI_ITEM_COUNT * sizeof(uint32_t))]; |
25 static unsigned int sbufidx; |
25 static unsigned int sbufidx; |
26 |
26 |
27 %(extern_variables_declarations)s |
27 %(extern_variables_declarations)s |
40 |
40 |
41 static int global_write_dirty = 0; |
41 static int global_write_dirty = 0; |
42 static long hmitree_rlock = 0; |
42 static long hmitree_rlock = 0; |
43 static long hmitree_wlock = 0; |
43 static long hmitree_wlock = 0; |
44 |
44 |
45 typedef struct { |
45 typedef struct hmi_tree_item_s hmi_tree_item_t; |
|
46 struct hmi_tree_item_s{ |
46 void *ptr; |
47 void *ptr; |
47 __IEC_types_enum type; |
48 __IEC_types_enum type; |
48 uint32_t buf_index; |
49 uint32_t buf_index; |
49 |
50 |
|
51 /* retrieve/read/recv */ |
|
52 buf_state_t rstate; |
|
53 |
50 /* publish/write/send */ |
54 /* publish/write/send */ |
51 buf_state_t wstate[MAX_CONNECTIONS]; |
55 buf_state_t wstate[MAX_CONNECTIONS]; |
52 |
56 |
53 /* zero means not subscribed */ |
57 /* zero means not subscribed */ |
54 uint16_t refresh_period_ms[MAX_CONNECTIONS]; |
58 uint16_t refresh_period_ms[MAX_CONNECTIONS]; |
55 uint16_t age_ms[MAX_CONNECTIONS]; |
59 uint16_t age_ms[MAX_CONNECTIONS]; |
56 |
60 |
57 /* retrieve/read/recv */ |
61 /* dual linked list for subscriptions */ |
58 buf_state_t rstate; |
62 hmi_tree_item_t *subscriptions_next; |
59 |
63 hmi_tree_item_t *subscriptions_prev; |
60 } hmi_tree_item_t; |
64 |
61 |
65 /* single linked list for changes from HMI */ |
62 static hmi_tree_item_t hmi_tree_item[] = { |
66 hmi_tree_item_t *incoming_prev; |
|
67 |
|
68 }; |
|
69 |
|
70 #define HMITREE_ITEM_INITIALIZER(cpath,type,buf_index) { \ |
|
71 &(cpath), /*ptr*/ \ |
|
72 type, /*type*/ \ |
|
73 buf_index, /*buf_index*/ \ |
|
74 buf_free, /*rstate*/ \ |
|
75 {[0 ... MAX_CON_INDEX] = buf_free}, /*wstate*/ \ |
|
76 {[0 ... MAX_CON_INDEX] = 0}, /*refresh_period_ms*/ \ |
|
77 {[0 ... MAX_CON_INDEX] = 0}, /*age_ms*/ \ |
|
78 NULL, /*subscriptions_next*/\ |
|
79 NULL, /*subscriptions_prev*/\ |
|
80 NULL} /*incoming_next*/ |
|
81 |
|
82 |
|
83 /* entry for dual linked list for HMI subscriptions */ |
|
84 /* points to the end of the list */ |
|
85 static hmi_tree_item_t *subscriptions_tail = NULL; |
|
86 |
|
87 /* entry for single linked list for changes from HMI */ |
|
88 /* points to the end of the list */ |
|
89 static hmi_tree_item_t *incoming_tail = NULL; |
|
90 |
|
91 static hmi_tree_item_t hmi_tree_items[] = { |
63 %(variable_decl_array)s |
92 %(variable_decl_array)s |
64 }; |
93 }; |
65 |
94 |
66 typedef int(*hmi_tree_iterator)(uint32_t, hmi_tree_item_t*); |
|
67 static int traverse_hmi_tree(hmi_tree_iterator fp) |
|
68 { |
|
69 unsigned int i; |
|
70 for(i = 0; i < sizeof(hmi_tree_item)/sizeof(hmi_tree_item_t); i++){ |
|
71 hmi_tree_item_t *dsc = &hmi_tree_item[i]; |
|
72 int res = (*fp)(i, dsc); |
|
73 if(res != 0){ |
|
74 return res; |
|
75 } |
|
76 } |
|
77 return 0; |
|
78 } |
|
79 |
|
80 #define __Unpack_desc_type hmi_tree_item_t |
95 #define __Unpack_desc_type hmi_tree_item_t |
81 |
96 |
82 %(var_access_code)s |
97 %(var_access_code)s |
83 |
98 |
84 static int write_iterator(uint32_t index, hmi_tree_item_t *dsc) |
99 static int write_iterator(hmi_tree_item_t *dsc) |
85 { |
100 { |
86 { |
101 { |
87 uint32_t session_index = 0; |
102 uint32_t session_index = 0; |
88 int value_changed = 0; |
103 int value_changed = 0; |
89 void *dest_p = NULL; |
104 void *dest_p = NULL; |
112 if(!value_changed){ |
127 if(!value_changed){ |
113 if(!value_p){ |
128 if(!value_p){ |
114 UnpackVar(dsc, &value_p, NULL, &sz); |
129 UnpackVar(dsc, &value_p, NULL, &sz); |
115 if(__Is_a_string(dsc)){ |
130 if(__Is_a_string(dsc)){ |
116 sz = ((STRING*)value_p)->len + 1; |
131 sz = ((STRING*)value_p)->len + 1; |
117 } |
132 } |
118 dest_p = &wbuf[dsc->buf_index]; |
133 dest_p = &wbuf[dsc->buf_index]; |
119 } |
134 } |
120 value_changed = memcmp(dest_p, value_p, sz) != 0; |
135 value_changed = memcmp(dest_p, value_p, sz) != 0; |
121 do_sample = value_changed; |
136 do_sample = value_changed; |
122 }else{ |
137 }else{ |
149 } |
164 } |
150 // else ... : PLC can't wait, variable will be updated next turn |
165 // else ... : PLC can't wait, variable will be updated next turn |
151 return 0; |
166 return 0; |
152 } |
167 } |
153 |
168 |
154 static uint32_t send_session_index; |
169 static int send_iterator(uint32_t index, hmi_tree_item_t *dsc, uint32_t session_index) |
155 static int send_iterator(uint32_t index, hmi_tree_item_t *dsc) |
170 { |
156 { |
171 if(dsc->wstate[session_index] == buf_tosend) |
157 if(dsc->wstate[send_session_index] == buf_tosend) |
|
158 { |
172 { |
159 uint32_t sz = __get_type_enum_size(dsc->type); |
173 uint32_t sz = __get_type_enum_size(dsc->type); |
160 if(sbufidx + sizeof(uint32_t) + sz <= sizeof(sbuf)) |
174 if(sbufidx + sizeof(uint32_t) + sz <= sizeof(sbuf)) |
161 { |
175 { |
162 void *src_p = &wbuf[dsc->buf_index]; |
176 void *src_p = &wbuf[dsc->buf_index]; |
165 sz = ((STRING*)src_p)->len + 1; |
179 sz = ((STRING*)src_p)->len + 1; |
166 } |
180 } |
167 /* TODO : force into little endian */ |
181 /* TODO : force into little endian */ |
168 memcpy(dst_p, &index, sizeof(uint32_t)); |
182 memcpy(dst_p, &index, sizeof(uint32_t)); |
169 memcpy(dst_p + sizeof(uint32_t), src_p, sz); |
183 memcpy(dst_p + sizeof(uint32_t), src_p, sz); |
170 dsc->wstate[send_session_index] = buf_free; |
184 dsc->wstate[session_index] = buf_free; |
171 sbufidx += sizeof(uint32_t) /* index */ + sz; |
185 sbufidx += sizeof(uint32_t) /* index */ + sz; |
172 } |
186 } |
173 else |
187 else |
174 { |
188 { |
175 printf("BUG!!! %%d + %%ld + %%d > %%ld \n", sbufidx, sizeof(uint32_t), sz, sizeof(sbuf)); |
189 printf("BUG!!! %%d + %%ld + %%d > %%ld \n", sbufidx, sizeof(uint32_t), sz, sizeof(sbuf)); |
194 return 0; |
208 return 0; |
195 } |
209 } |
196 |
210 |
197 void update_refresh_period(hmi_tree_item_t *dsc, uint32_t session_index, uint16_t refresh_period_ms) |
211 void update_refresh_period(hmi_tree_item_t *dsc, uint32_t session_index, uint16_t refresh_period_ms) |
198 { |
212 { |
199 if(refresh_period_ms) { |
213 uint32_t other_session_index = 0; |
200 if(!dsc->refresh_period_ms[session_index]) |
214 int previously_subscribed = 0; |
|
215 int session_only_subscriber = 0; |
|
216 int session_subscriber = 0; |
|
217 int needs_subscription_for_session = (refresh_period_ms != 0); |
|
218 |
|
219 while(other_session_index < session_index) { |
|
220 previously_subscribed |= (dsc->refresh_period_ms[other_session_index++] != 0); |
|
221 } |
|
222 session_subscriber = (dsc->refresh_period_ms[other_session_index++] != 0); |
|
223 while(other_session_index < MAX_CONNECTIONS) { |
|
224 previously_subscribed |= (dsc->refresh_period_ms[other_session_index++] != 0); |
|
225 } |
|
226 session_only_subscriber = session_subscriber && !previously_subscribed; |
|
227 previously_subscribed |= session_subscriber; |
|
228 |
|
229 printf("update_refresh_period %%x,%%x index:%%d session_index:%%d refresh_period_ms:%%d\n", |
|
230 dsc, |
|
231 hmi_tree_items, |
|
232 dsc - hmi_tree_items, |
|
233 session_index, |
|
234 refresh_period_ms); |
|
235 |
|
236 if(needs_subscription_for_session) { |
|
237 if(!session_subscriber) |
201 { |
238 { |
202 dsc->wstate[session_index] = buf_new; |
239 dsc->wstate[session_index] = buf_new; |
203 } |
240 } |
|
241 /* if not already subscribed */ |
|
242 if(!previously_subscribed){ |
|
243 /* append subsciption to list */ |
|
244 dsc->subscriptions_prev = subscriptions_tail; |
|
245 subscriptions_tail = dsc; |
|
246 dsc->subscriptions_next = NULL; |
|
247 } |
204 } else { |
248 } else { |
205 dsc->wstate[session_index] = buf_free; |
249 dsc->wstate[session_index] = buf_free; |
|
250 /* item is removed from list only when session was the only one remaining */ |
|
251 if (session_only_subscriber) { |
|
252 if(dsc->subscriptions_next == NULL){ /* remove tail */ |
|
253 /* re-link tail to previous */ |
|
254 subscriptions_tail = dsc->subscriptions_prev; |
|
255 } else { /* remove entry in between other entries */ |
|
256 /* re-link previous iand next node */ |
|
257 dsc->subscriptions_next->subscriptions_prev = dsc->subscriptions_prev; |
|
258 dsc->subscriptions_prev->subscriptions_next = dsc->subscriptions_next; |
|
259 } |
|
260 /* unnecessary |
|
261 dsc->subscriptions_next = NULL; |
|
262 dsc->subscriptions_prev = NULL; |
|
263 */ |
|
264 } |
206 } |
265 } |
207 dsc->refresh_period_ms[session_index] = refresh_period_ms; |
266 dsc->refresh_period_ms[session_index] = refresh_period_ms; |
208 } |
|
209 |
|
210 static uint32_t reset_session_index; |
|
211 static int reset_iterator(uint32_t index, hmi_tree_item_t *dsc) |
|
212 { |
|
213 update_refresh_period(dsc, reset_session_index, 0); |
|
214 return 0; |
|
215 } |
267 } |
216 |
268 |
217 static void *svghmi_handle; |
269 static void *svghmi_handle; |
218 |
270 |
219 void SVGHMI_SuspendFromPythonThread(void) |
271 void SVGHMI_SuspendFromPythonThread(void) |
252 } |
304 } |
253 |
305 |
254 void __retrieve_svghmi() |
306 void __retrieve_svghmi() |
255 { |
307 { |
256 if(AtomicCompareExchange(&hmitree_rlock, 0, 1) == 0) { |
308 if(AtomicCompareExchange(&hmitree_rlock, 0, 1) == 0) { |
257 traverse_hmi_tree(read_iterator); |
309 hmi_tree_item_t *dsc = incoming_tail; |
|
310 /* iterate through read list (changes from HMI) */ |
|
311 while(dsc){ |
|
312 read_iterator(dsc); |
|
313 dsc = dsc->incoming_prev; |
|
314 /* unnecessary |
|
315 dsc->incoming_prev = NULL; |
|
316 */ |
|
317 } |
|
318 /* flush read list */ |
|
319 incoming_tail = NULL; |
258 AtomicCompareExchange(&hmitree_rlock, 1, 0); |
320 AtomicCompareExchange(&hmitree_rlock, 1, 0); |
259 } |
321 } |
260 } |
322 } |
261 |
323 |
262 void __publish_svghmi() |
324 void __publish_svghmi() |
263 { |
325 { |
264 global_write_dirty = 0; |
326 global_write_dirty = 0; |
|
327 |
265 if(AtomicCompareExchange(&hmitree_wlock, 0, 1) == 0) { |
328 if(AtomicCompareExchange(&hmitree_wlock, 0, 1) == 0) { |
266 traverse_hmi_tree(write_iterator); |
329 hmi_tree_item_t *dsc = subscriptions_tail; |
|
330 while(dsc){ |
|
331 write_iterator(dsc); |
|
332 dsc = dsc->subscriptions_prev; |
|
333 } |
267 AtomicCompareExchange(&hmitree_wlock, 1, 0); |
334 AtomicCompareExchange(&hmitree_wlock, 1, 0); |
268 } |
335 } |
|
336 |
269 if(global_write_dirty) { |
337 if(global_write_dirty) { |
270 SVGHMI_WakeupFromRTThread(); |
338 SVGHMI_WakeupFromRTThread(); |
271 } |
339 } |
272 } |
340 } |
273 |
341 |
280 int svghmi_send_collect(uint32_t session_index, uint32_t *size, char **ptr){ |
348 int svghmi_send_collect(uint32_t session_index, uint32_t *size, char **ptr){ |
281 |
349 |
282 if(svghmi_continue_collect) { |
350 if(svghmi_continue_collect) { |
283 int res; |
351 int res; |
284 sbufidx = HMI_HASH_SIZE; |
352 sbufidx = HMI_HASH_SIZE; |
285 send_session_index = session_index; |
|
286 |
353 |
287 while(AtomicCompareExchange(&hmitree_wlock, 0, 1)){ |
354 while(AtomicCompareExchange(&hmitree_wlock, 0, 1)){ |
288 nRT_reschedule(); |
355 nRT_reschedule(); |
289 } |
356 } |
290 |
357 |
291 if((res = traverse_hmi_tree(send_iterator)) == 0) |
358 hmi_tree_item_t *dsc = subscriptions_tail; |
|
359 while(dsc){ |
|
360 uint32_t index = dsc - hmi_tree_items; |
|
361 printf("Send index %%d\n", index); |
|
362 res = send_iterator(index, dsc, session_index); |
|
363 if(res != 0){ |
|
364 break; |
|
365 } |
|
366 dsc = dsc->subscriptions_prev; |
|
367 } |
|
368 if(res == 0) |
292 { |
369 { |
293 if(sbufidx > HMI_HASH_SIZE){ |
370 if(sbufidx > HMI_HASH_SIZE){ |
294 memcpy(&sbuf[0], &hmi_hash[0], HMI_HASH_SIZE); |
371 memcpy(&sbuf[0], &hmi_hash[0], HMI_HASH_SIZE); |
295 *ptr = &sbuf[0]; |
372 *ptr = &sbuf[0]; |
296 *size = sbufidx; |
373 *size = sbufidx; |
371 if(index == heartbeat_index) |
451 if(index == heartbeat_index) |
372 was_hearbeat = 1; |
452 was_hearbeat = 1; |
373 |
453 |
374 if(index < HMI_ITEM_COUNT) |
454 if(index < HMI_ITEM_COUNT) |
375 { |
455 { |
376 hmi_tree_item_t *dsc = &hmi_tree_item[index]; |
456 hmi_tree_item_t *dsc = &hmi_tree_items[index]; |
377 void *value_p = NULL; |
|
378 size_t sz = 0; |
457 size_t sz = 0; |
379 UnpackVar(dsc, &value_p, NULL, &sz); |
|
380 void *dst_p = &rbuf[dsc->buf_index]; |
458 void *dst_p = &rbuf[dsc->buf_index]; |
381 |
459 |
382 if(__Is_a_string(dsc)){ |
460 if(__Is_a_string(dsc)){ |
383 sz = ((STRING*)valptr)->len + 1; |
461 sz = ((STRING*)valptr)->len + 1; |
|
462 } else { |
|
463 UnpackVar(dsc, NULL, NULL, &sz); |
384 } |
464 } |
385 |
465 |
386 if((valptr + sz) <= end) |
466 if((valptr + sz) <= end) |
387 { |
467 { |
388 // rescheduling spinlock until free |
468 // rescheduling spinlock until free |