2 #include "iec_types_all.h" |
2 #include "iec_types_all.h" |
3 #include "POUS.h" |
3 #include "POUS.h" |
4 #include "config.h" |
4 #include "config.h" |
5 #include "beremiz.h" |
5 #include "beremiz.h" |
6 |
6 |
|
7 #define DEFAULT_REFRESH_PERIOD_MS 100 |
7 #define HMI_BUFFER_SIZE %(buffer_size)d |
8 #define HMI_BUFFER_SIZE %(buffer_size)d |
8 |
9 |
9 /* PLC reads from that buffer */ |
10 /* PLC reads from that buffer */ |
10 static char rbuf[HMI_BUFFER_SIZE]; |
11 static char rbuf[HMI_BUFFER_SIZE]; |
11 |
12 |
12 /* PLC writes to that buffer */ |
13 /* PLC writes to that buffer */ |
13 static char wbuf[HMI_BUFFER_SIZE]; |
14 static char wbuf[HMI_BUFFER_SIZE]; |
14 |
15 |
15 static pthread_mutex_t wbuf_mutex = PTHREAD_MUTEX_INITIALIZER; |
16 %(extern_variables_declarations)s |
16 static pthread_mutex_t rbuf_mutex = PTHREAD_MUTEX_INITIALIZER; |
|
17 |
17 |
18 %(extern_variables_declarations)s |
18 #define ticktime_ns %(PLC_ticktime)d; |
|
19 uint16_t ticktime_ms (ticktime_ns>1000000)? |
|
20 ticktime_ns/1000000: |
|
21 1; |
|
22 |
|
23 typedef enum { |
|
24 buf_free = 0, |
|
25 buf_set, |
|
26 buf_tosend |
|
27 } buf_state_t; |
|
28 |
|
29 int global_write_dirty = 0; |
19 |
30 |
20 typedef const struct { |
31 typedef const struct { |
21 void *ptr; |
32 void *ptr; |
22 __IEC_types_enum type; |
33 __IEC_types_enum type; |
23 uint32_t buf_index; |
34 uint32_t buf_index; |
24 uint32_t flags; |
35 |
|
36 /* publish/write/send */ |
|
37 int wlock; |
|
38 /* zero means not subscribed */ |
|
39 uint16_t refresh_period_ms; |
|
40 uint16_t age_ms; |
|
41 |
|
42 buf_state_t wstate; |
|
43 |
|
44 /* retrieve/read/recv */ |
|
45 int rlock; |
|
46 buf_state_t rstate; |
|
47 |
25 } hmi_tree_item_t; |
48 } hmi_tree_item_t; |
26 |
49 |
27 static hmi_tree_item_t hmi_tree_item[] = { |
50 static hmi_tree_item_t hmi_tree_item[] = { |
28 %(variable_decl_array)s |
51 %(variable_decl_array)s |
29 }; |
52 }; |
49 void *real_value_p = NULL; |
72 void *real_value_p = NULL; |
50 char flags = 0; |
73 char flags = 0; |
51 |
74 |
52 void *visible_value_p = UnpackVar(dsc, &real_value_p, &flags); |
75 void *visible_value_p = UnpackVar(dsc, &real_value_p, &flags); |
53 |
76 |
54 memcpy(dest_p, visible_value_p, __get_type_enum_size(dsc->type)); |
77 /* Try take lock */ |
|
78 long was_locked = AtomicCompareExchange(&dsc->wlock, 0, 1); |
|
79 |
|
80 if(was_locked) { |
|
81 /* was locked. give up*/ |
|
82 return; |
|
83 } |
|
84 |
|
85 if(dsc->wstate == buf_set) |
|
86 /* if being subscribed */ |
|
87 if(dsc->refresh_period_ms){ |
|
88 if(dsc->age_ms + ticktime_ms < dsc->refresh_period_ms){ |
|
89 dsc->age_ms += ticktime_ms; |
|
90 }else{ |
|
91 dsc->wstate = buf_tosend; |
|
92 } |
|
93 } |
|
94 } |
|
95 |
|
96 /* if new value differs from previous one */ |
|
97 if(memcmp(dest_p, visible_value_p, __get_type_enum_size(dsc->type)) != 0){ |
|
98 /* copy and flag as set */ |
|
99 memcpy(dest_p, visible_value_p, __get_type_enum_size(dsc->type)); |
|
100 if(dsc->wstate == buf_free) { |
|
101 dsc->wstate = buf_set; |
|
102 dsc->age_ms = 0; |
|
103 } |
|
104 global_write_dirty = 1; |
|
105 } |
|
106 |
|
107 /* unlock - use AtomicComparExchange to have memory barrier */ |
|
108 AtomicCompareExchange(&dsc->wlock, 1, 0); |
|
109 } |
|
110 |
|
111 struct timespec sending_now; |
|
112 struct timespec next_sending; |
|
113 void send_iterator(hmi_tree_item_t *dsc) |
|
114 { |
|
115 while(AtomicCompareExchange(&dsc->wlock, 0, 1)) sched_yield(); |
|
116 |
|
117 // check for variable being modified |
|
118 if(dsc->wstat == buf_tosend){ |
|
119 // send |
|
120 |
|
121 // TODO write to some socket |
|
122 |
|
123 dsc->wstate = buf_free; |
|
124 } |
|
125 |
|
126 AtomicCompareExchange(&dsc->wlock, 1, 0); |
55 } |
127 } |
56 |
128 |
57 void read_iterator(hmi_tree_item_t *dsc) |
129 void read_iterator(hmi_tree_item_t *dsc) |
58 { |
130 { |
59 void *src_p = &rbuf[dsc->buf_index]; |
131 void *src_p = &rbuf[dsc->buf_index]; |
60 void *real_value_p = NULL; |
132 void *real_value_p = NULL; |
61 char flags = 0; |
133 char flags = 0; |
62 |
134 |
63 void *visible_value_p = UnpackVar(dsc, &real_value_p, &flags); |
135 void *visible_value_p = UnpackVar(dsc, &real_value_p, &flags); |
64 |
136 |
|
137 |
65 memcpy(visible_value_p, src_p, __get_type_enum_size(dsc->type)); |
138 memcpy(visible_value_p, src_p, __get_type_enum_size(dsc->type)); |
66 } |
139 } |
67 |
140 |
68 int __init_svghmi() |
141 int __init_svghmi() |
69 { |
142 { |
70 bzero(rbuf,sizeof(rbuf)); |
143 bzero(rbuf,sizeof(rbuf)); |
71 bzero(wbuf,sizeof(wbuf)); |
144 bzero(wbuf,sizeof(wbuf)); |
|
145 |
|
146 // create - connection endpoint |
|
147 // - sending thread |
|
148 // - sending semaphore |
|
149 // - recv thread |
72 |
150 |
73 return 0; |
151 return 0; |
74 } |
152 } |
75 |
153 |
76 void __cleanup_svghmi() |
154 void __cleanup_svghmi() |
77 { |
155 { |
78 } |
156 } |
79 |
157 |
80 void __retrieve_svghmi() |
158 void __retrieve_svghmi() |
81 { |
159 { |
82 if(!pthread_mutex_lock(&rbuf_mutex)){ |
160 traverse_hmi_tree(read_iterator); |
83 traverse_hmi_tree(read_iterator); |
|
84 pthread_mutex_unlock(&rbuf_mutex); |
|
85 } |
|
86 } |
161 } |
87 |
162 |
88 void __publish_svghmi() |
163 void __publish_svghmi() |
89 { |
164 { |
90 if(!pthread_mutex_lock(&wbuf_mutex)){ |
165 global_write_dirty = 0; |
91 pthread_mutex_unlock(&wbuf_mutex); |
166 traverse_hmi_tree(write_iterator); |
|
167 if(global_write_dirty) { |
|
168 // TODO : set emaphore to wakeup sending thread |
92 } |
169 } |
|
170 |
93 } |
171 } |
94 |
172 |
|
173 void sending_thread_proc(void* args){ |
|
174 |
|
175 // TODO : wait for |
|
176 // - semaphore |
|
177 // - next autonomous send thread wakeup. (impl as wait timeout ?) |
|
178 |
|
179 traverse_hmi_tree(send_iterator); |
|
180 } |