6 #include "beremiz.h" |
6 #include "beremiz.h" |
7 |
7 |
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 |
|
12 static uint8_t hmi_hash[HMI_HASH_SIZE] = {%(hmi_hash_ints)s}; |
11 |
13 |
12 /* PLC reads from that buffer */ |
14 /* PLC reads from that buffer */ |
13 static char rbuf[HMI_BUFFER_SIZE]; |
15 static char rbuf[HMI_BUFFER_SIZE]; |
14 |
16 |
15 /* PLC writes to that buffer */ |
17 /* PLC writes to that buffer */ |
16 static char wbuf[HMI_BUFFER_SIZE]; |
18 static char wbuf[HMI_BUFFER_SIZE]; |
17 |
19 |
|
20 /* TODO change that in case of multiclient... */ |
|
21 /* worst biggest send buffer. FIXME : use dynamic alloc ? */ |
|
22 static char sbuf[HMI_HASH_SIZE + HMI_BUFFER_SIZE + (HMI_ITEM_COUNT * sizeof(uint32_t))]; |
|
23 static unsigned int sbufidx; |
|
24 |
18 %(extern_variables_declarations)s |
25 %(extern_variables_declarations)s |
19 |
26 |
20 #define ticktime_ns %(PLC_ticktime)d |
27 #define ticktime_ns %(PLC_ticktime)d |
21 uint16_t ticktime_ms = (ticktime_ns>1000000)? |
28 static uint16_t ticktime_ms = (ticktime_ns>1000000)? |
22 ticktime_ns/1000000: |
29 ticktime_ns/1000000: |
23 1; |
30 1; |
24 |
31 |
25 typedef enum { |
32 typedef enum { |
26 buf_free = 0, |
33 buf_free = 0, |
27 buf_set, |
34 buf_set, |
28 buf_tosend |
35 buf_tosend |
29 } buf_state_t; |
36 } buf_state_t; |
30 |
37 |
31 int global_write_dirty = 0; |
38 static int global_write_dirty = 0; |
32 |
39 |
33 typedef struct { |
40 typedef struct { |
34 void *ptr; |
41 void *ptr; |
35 __IEC_types_enum type; |
42 __IEC_types_enum type; |
36 uint32_t buf_index; |
43 uint32_t buf_index; |
37 |
44 |
38 /* publish/write/send */ |
45 /* publish/write/send */ |
39 long wlock; |
46 long wlock; |
|
47 buf_state_t wstate; |
|
48 |
40 /* zero means not subscribed */ |
49 /* zero means not subscribed */ |
41 uint16_t refresh_period_ms; |
50 uint16_t refresh_period_ms; |
42 uint16_t age_ms; |
51 uint16_t age_ms; |
43 |
52 |
44 buf_state_t wstate; |
|
45 |
|
46 /* retrieve/read/recv */ |
53 /* retrieve/read/recv */ |
47 long rlock; |
54 long rlock; |
48 buf_state_t rstate; |
55 buf_state_t rstate; |
49 |
56 |
50 } hmi_tree_item_t; |
57 } hmi_tree_item_t; |
51 |
58 |
52 static hmi_tree_item_t hmi_tree_item[] = { |
59 static hmi_tree_item_t hmi_tree_item[] = { |
53 %(variable_decl_array)s |
60 %(variable_decl_array)s |
54 }; |
61 }; |
55 |
62 |
56 static char sendbuf[HMI_BUFFER_SIZE]; |
63 typedef int(*hmi_tree_iterator)(uint32_t*, hmi_tree_item_t*); |
57 |
64 static int traverse_hmi_tree(hmi_tree_iterator fp) |
58 typedef void(*hmi_tree_iterator)(hmi_tree_item_t*); |
|
59 void traverse_hmi_tree(hmi_tree_iterator fp) |
|
60 { |
65 { |
61 unsigned int i; |
66 unsigned int i; |
62 for(i = 0; i < sizeof(hmi_tree_item)/sizeof(hmi_tree_item_t); i++){ |
67 for(i = 0; i < sizeof(hmi_tree_item)/sizeof(hmi_tree_item_t); i++){ |
|
68 int res; |
63 hmi_tree_item_t *dsc = &hmi_tree_item[i]; |
69 hmi_tree_item_t *dsc = &hmi_tree_item[i]; |
64 if(dsc->type != UNKNOWN_ENUM) |
70 if(res = (*fp)(i, dsc)) |
65 (*fp)(dsc); |
71 return res; |
66 } |
72 } |
67 } |
73 } |
68 |
74 |
69 #define __Unpack_desc_type hmi_tree_item_t |
75 #define __Unpack_desc_type hmi_tree_item_t |
70 |
76 |
71 %(var_access_code)s |
77 %(var_access_code)s |
72 |
78 |
73 void write_iterator(hmi_tree_item_t *dsc) |
79 inline int write_iterator(uint32_t index, hmi_tree_item_t *dsc) |
74 { |
80 { |
75 void *dest_p = &wbuf[dsc->buf_index]; |
81 if(AtomicCompareExchange(&dsc->wlock, 0, 1) == 0) |
76 void *real_value_p = NULL; |
82 { |
77 char flags = 0; |
83 if(dsc->wstate == buf_set){ |
78 |
84 /* if being subscribed */ |
79 void *visible_value_p = UnpackVar(dsc, &real_value_p, &flags); |
85 if(dsc->refresh_period_ms){ |
80 |
86 if(dsc->age_ms + ticktime_ms < dsc->refresh_period_ms){ |
81 /* Try take lock */ |
87 dsc->age_ms += ticktime_ms; |
82 long was_locked = AtomicCompareExchange(&dsc->wlock, 0, 1); |
88 }else{ |
83 |
89 dsc->wstate = buf_tosend; |
84 if(was_locked) { |
90 } |
85 /* was locked. give up*/ |
91 } |
86 return; |
92 } |
87 } |
93 |
88 |
94 void *dest_p = &wbuf[dsc->buf_index]; |
89 if(dsc->wstate == buf_set){ |
95 void *real_value_p = NULL; |
90 /* if being subscribed */ |
96 char flags = 0; |
91 if(dsc->refresh_period_ms){ |
97 void *visible_value_p = UnpackVar(dsc, &real_value_p, &flags); |
92 if(dsc->age_ms + ticktime_ms < dsc->refresh_period_ms){ |
98 |
93 dsc->age_ms += ticktime_ms; |
99 /* if new value differs from previous one */ |
94 }else{ |
100 USINT sz = __get_type_enum_size(dsc->type); |
95 dsc->wstate = buf_tosend; |
101 if(memcmp(dest_p, visible_value_p, sz) != 0){ |
96 } |
102 /* copy and flag as set */ |
97 } |
103 memcpy(dest_p, visible_value_p, sz); |
98 } |
104 if(dsc->wstate == buf_free) { |
99 |
105 dsc->wstate = buf_set; |
100 /* if new value differs from previous one */ |
106 dsc->age_ms = 0; |
101 if(memcmp(dest_p, visible_value_p, __get_type_enum_size(dsc->type)) != 0){ |
107 } |
102 /* copy and flag as set */ |
108 global_write_dirty = 1; |
103 memcpy(dest_p, visible_value_p, __get_type_enum_size(dsc->type)); |
109 } |
104 if(dsc->wstate == buf_free) { |
110 |
105 dsc->wstate = buf_set; |
111 AtomicCompareExchange(&dsc->wlock, 1, 0); |
106 dsc->age_ms = 0; |
112 } |
107 } |
113 // else ... : PLC can't wait, variable will be updated next turn |
108 global_write_dirty = 1; |
114 return 0; |
109 } |
115 } |
110 |
116 |
111 /* unlock - use AtomicComparExchange to have memory barrier */ |
117 inline int send_iterator(uint32_t index, hmi_tree_item_t *dsc) |
|
118 { |
|
119 int res = 0; |
|
120 while(AtomicCompareExchange(&dsc->wlock, 0, 1)) sched_yield(); |
|
121 |
|
122 if(dsc->wstate == buf_tosend) |
|
123 { |
|
124 uint32_t sz = __get_type_enum_size(dsc->type); |
|
125 if(sbufidx + sizeof(uint32_t) + sz < sizeof(sbuf)) |
|
126 { |
|
127 void *src_p = &wbuf[dsc->buf_index]; |
|
128 void *dst_p = &sbuf[sbufidx]; |
|
129 memcpy(dst_p, &index, sizeof(uint32_t)); |
|
130 memcpy(dst_p + sizeof(uint32_t), src_p, sz); |
|
131 dsc->wstate = buf_free; |
|
132 sbufidx += sizeof(uint32_t) /* index */ + sz; |
|
133 } |
|
134 else |
|
135 { |
|
136 res = EOVERFLOW; |
|
137 } |
|
138 } |
|
139 |
112 AtomicCompareExchange(&dsc->wlock, 1, 0); |
140 AtomicCompareExchange(&dsc->wlock, 1, 0); |
113 } |
141 return res; |
114 |
142 } |
115 struct timespec sending_now; |
143 |
116 struct timespec next_sending; |
144 inline int read_iterator(uint32_t index, hmi_tree_item_t *dsc) |
117 void send_iterator(hmi_tree_item_t *dsc) |
145 { |
|
146 if(AtomicCompareExchange(&dsc->rlock, 0, 1) == 0) |
|
147 { |
|
148 if(dsc->rstate == buf_set) |
|
149 { |
|
150 void *src_p = &rbuf[dsc->buf_index]; |
|
151 void *real_value_p = NULL; |
|
152 char flags = 0; |
|
153 void *visible_value_p = UnpackVar(dsc, &real_value_p, &flags); |
|
154 memcpy(real_value_p, src_p, __get_type_enum_size(dsc->type)); |
|
155 dsc->rstate = buf_free; |
|
156 } |
|
157 AtomicCompareExchange(&dsc->rlock, 1, 0); |
|
158 } |
|
159 // else ... : PLC can't wait, variable will be updated next turn |
|
160 return 0; |
|
161 } |
|
162 |
|
163 inline void update_refresh_period(hmi_tree_item_t *dsc, uint16_t refresh_period_ms) |
118 { |
164 { |
119 while(AtomicCompareExchange(&dsc->wlock, 0, 1)) sched_yield(); |
165 while(AtomicCompareExchange(&dsc->wlock, 0, 1)) sched_yield(); |
120 |
166 dsc->refresh_period_ms = refresh_period_ms; |
121 // check for variable being modified |
|
122 if(dsc->wstate == buf_tosend){ |
|
123 // send |
|
124 |
|
125 // TODO pack data in buffer |
|
126 |
|
127 dsc->wstate = buf_free; |
|
128 } |
|
129 |
|
130 AtomicCompareExchange(&dsc->wlock, 1, 0); |
167 AtomicCompareExchange(&dsc->wlock, 1, 0); |
131 } |
168 } |
132 |
169 |
133 void read_iterator(hmi_tree_item_t *dsc) |
170 inline int reset_iterator(uint32_t index, hmi_tree_item_t *dsc) |
134 { |
171 { |
135 void *src_p = &rbuf[dsc->buf_index]; |
172 update_refresh_period(*dsc, 0); |
136 void *real_value_p = NULL; |
173 return 0; |
137 char flags = 0; |
|
138 |
|
139 void *visible_value_p = UnpackVar(dsc, &real_value_p, &flags); |
|
140 |
|
141 |
|
142 memcpy(visible_value_p, src_p, __get_type_enum_size(dsc->type)); |
|
143 } |
174 } |
144 |
175 |
145 static pthread_cond_t svghmi_send_WakeCond = PTHREAD_COND_INITIALIZER; |
176 static pthread_cond_t svghmi_send_WakeCond = PTHREAD_COND_INITIALIZER; |
146 static pthread_mutex_t svghmi_send_WakeCondLock = PTHREAD_MUTEX_INITIALIZER; |
177 static pthread_mutex_t svghmi_send_WakeCondLock = PTHREAD_MUTEX_INITIALIZER; |
147 |
178 |
182 int svghmi_send_collect(uint32_t *size, char **ptr){ |
213 int svghmi_send_collect(uint32_t *size, char **ptr){ |
183 |
214 |
184 int do_collect; |
215 int do_collect; |
185 pthread_mutex_lock(&svghmi_send_WakeCondLock); |
216 pthread_mutex_lock(&svghmi_send_WakeCondLock); |
186 do_collect = continue_collect; |
217 do_collect = continue_collect; |
187 if(do_collect){ |
218 if(do_collect) |
|
219 { |
188 pthread_cond_wait(&svghmi_send_WakeCond, &svghmi_send_WakeCondLock); |
220 pthread_cond_wait(&svghmi_send_WakeCond, &svghmi_send_WakeCondLock); |
189 do_collect = continue_collect; |
221 do_collect = continue_collect; |
190 } |
222 } |
191 pthread_mutex_unlock(&svghmi_send_WakeCondLock); |
223 pthread_mutex_unlock(&svghmi_send_WakeCondLock); |
192 |
224 |
193 |
|
194 if(do_collect) { |
225 if(do_collect) { |
195 traverse_hmi_tree(send_iterator); |
226 int res; |
196 /* TODO set ptr and size to something */ |
227 memcpy(&sbuf[0], &hmi_hash[0], HMI_HASH_SIZE); |
197 return 0; |
228 sbufidx = HMI_HASH_SIZE; |
|
229 if((res = traverse_hmi_tree(send_iterator)) == 0) |
|
230 { |
|
231 *ptr = &sbuf[0]; |
|
232 *size = sbufidx; |
|
233 } |
|
234 return res; |
198 } |
235 } |
199 else |
236 else |
200 { |
237 { |
201 return EINTR; |
238 return EINTR; |
202 } |
239 } |
203 } |
240 } |
204 |
241 |
205 int svghmi_recv_dispatch(uint32_t size, char *ptr){ |
242 typedef enum { |
206 printf("%%*s",size,ptr); |
243 setval = 0, |
207 /* TODO something with ptr and size |
244 reset = 1, |
208 - subscribe |
245 subscribe = 2, |
209 or |
246 unsubscribe = 3 |
210 - spread values |
247 } cmd_from_JS; |
211 */ |
248 |
212 } |
249 int svghmi_recv_dispatch(uint32_t size, const uint8_t *ptr){ |
213 |
250 const uint8_t* cursor = ptr + HMI_HASH_SIZE; |
|
251 const uint8_t* end = ptr + size; |
|
252 |
|
253 printf("svghmi_recv_dispatch %d\n",size); |
|
254 |
|
255 /* match hmitree fingerprint */ |
|
256 if(size <= HMI_HASH_SIZE || memcmp(ptr, hmihash, HMI_HASH_SIZE) != 0) |
|
257 { |
|
258 printf("svghmi_recv_dispatch MISMATCH !!\n"); |
|
259 return EINVAL; |
|
260 } |
|
261 |
|
262 while(cursor < end) |
|
263 { |
|
264 uint32_t progress; |
|
265 cmd_from_JS cmd = *(cursor++); |
|
266 switch(cmd) |
|
267 { |
|
268 case setval: |
|
269 { |
|
270 uint32_t index = *(uint32_t*)(cursor); |
|
271 uint8_t *valptr = cursor + sizeof(uint32_t); |
|
272 |
|
273 if(index < HMI_ITEM_COUNT) |
|
274 { |
|
275 hmi_tree_item_t *dsc = &hmi_tree_item[index]; |
|
276 void *real_value_p = NULL; |
|
277 char flags = 0; |
|
278 void *visible_value_p = UnpackVar(dsc, &real_value_p, &flags); |
|
279 void *dst_p = &rbuf[dsc->buf_index]; |
|
280 uint32_t sz = __get_type_enum_size(dsc->type); |
|
281 |
|
282 if(valptr + sz < end) |
|
283 { |
|
284 // rescheduling spinlock until free |
|
285 while(AtomicCompareExchange(&dsc->rlock, 0, 1)) sched_yield(); |
|
286 |
|
287 memcpy(dst_p, valptr, sz); |
|
288 dsc->rstate = buf_set; |
|
289 |
|
290 AtomicCompareExchange(&dsc->rlock, 1, 0); |
|
291 progress = sz + sizeof(uint32_t) /* index */; |
|
292 } |
|
293 else return -EINVAL; |
|
294 } |
|
295 else return -EINVAL; |
|
296 } |
|
297 break; |
|
298 |
|
299 case reset: |
|
300 { |
|
301 progress = 0; |
|
302 traverse_hmi_tree(reset_iterator); |
|
303 } |
|
304 break; |
|
305 |
|
306 case subscribe: |
|
307 { |
|
308 uint32_t index = *(uint32_t*)(cursor); |
|
309 uint16_t refresh_period_ms = *(uint32_t*)(cursor + sizeof(uint32_t)); |
|
310 |
|
311 if(index < HMI_ITEM_COUNT) |
|
312 { |
|
313 hmi_tree_item_t *dsc = &hmi_tree_item[index]; |
|
314 update_refresh_period(*dsc, refresh_period_ms); |
|
315 } |
|
316 else return -EINVAL; |
|
317 |
|
318 progress = sizeof(uint32_t) /* index */ + |
|
319 sizeof(uint16_t) /* refresh period */; |
|
320 } |
|
321 break; |
|
322 |
|
323 case unsubscribe: |
|
324 { |
|
325 if(index < HMI_ITEM_COUNT) |
|
326 { |
|
327 hmi_tree_item_t *dsc = &hmi_tree_item[index]; |
|
328 reset_iterator(index, dsc); |
|
329 } |
|
330 else return -EINVAL; |
|
331 |
|
332 progress = sizeof(uint32_t) /* index */; |
|
333 } |
|
334 break; |
|
335 } |
|
336 cursor += progress; |
|
337 } |
|
338 return 0; |
|
339 } |
|
340 |