|
1 /* |
|
2 * Python Asynchronous execution code |
|
3 * |
|
4 * PLC put python commands in a fifo, respecting execution order |
|
5 * with the help of C pragmas inserted in python_eval FB code |
|
6 * |
|
7 * Buffer content is read asynchronously, (from non real time part), |
|
8 * commands are executed and result stored for later use by PLC. |
|
9 * |
|
10 * In this implementation, fifo is a list of pointer to python_eval |
|
11 * function blocks structures. Some local variables have been added in |
|
12 * python_eval interface. We use those local variables as buffer and state |
|
13 * flags. |
|
14 * |
|
15 * */ |
|
16 |
|
17 #include "iec_types_all.h" |
|
18 #include "POUS.h" |
|
19 #include <string.h> |
|
20 |
|
21 /* The fifo (fixed size, as number of FB is fixed) */ |
|
22 static PYTHON_EVAL* EvalFBs[%(python_eval_fb_count)d]; |
|
23 /* Producer and consumer cursors */ |
|
24 static long Current_PLC_EvalFB; |
|
25 static long Current_Python_EvalFB; |
|
26 |
|
27 /* A global IEC-Python gateway state, for use inside python_eval FBs*/ |
|
28 static int PythonState; |
|
29 #define PYTHON_LOCKED_BY_PYTHON 0 |
|
30 #define PYTHON_LOCKED_BY_PLC 1 |
|
31 #define PYTHON_MUSTWAKEUP 2 |
|
32 |
|
33 /* Each python_eval FunctionBlock have it own state */ |
|
34 #define PYTHON_FB_FREE 0 |
|
35 #define PYTHON_FB_REQUESTED 1 |
|
36 #define PYTHON_FB_PROCESSING 2 |
|
37 #define PYTHON_FB_ANSWERED 3 |
|
38 |
|
39 int WaitPythonCommands(void); |
|
40 void UnBlockPythonCommands(void); |
|
41 int TryLockPython(void); |
|
42 void UnLockPython(void); |
|
43 void LockPython(void); |
|
44 |
|
45 void __init_python() |
|
46 { |
|
47 int i; |
|
48 /* Initialize cursors */ |
|
49 Current_Python_EvalFB = 0; |
|
50 Current_PLC_EvalFB = 0; |
|
51 PythonState = PYTHON_LOCKED_BY_PYTHON; |
|
52 for(i = 0; i < %(python_eval_fb_count)d; i++) |
|
53 EvalFBs[i] = NULL; |
|
54 } |
|
55 |
|
56 void __cleanup_python() |
|
57 { |
|
58 } |
|
59 |
|
60 void __retrieve_python() |
|
61 { |
|
62 /* Check Python thread is not being |
|
63 * modifying internal python_eval data */ |
|
64 PythonState = TryLockPython() ? |
|
65 PYTHON_LOCKED_BY_PLC : |
|
66 PYTHON_LOCKED_BY_PYTHON; |
|
67 /* If python thread _is_ in, then PythonState remains PYTHON_LOCKED_BY_PYTHON |
|
68 * and python_eval will no do anything */ |
|
69 } |
|
70 |
|
71 void __publish_python() |
|
72 { |
|
73 if(PythonState & PYTHON_LOCKED_BY_PLC){ |
|
74 /* If runnig PLC did push something in the fifo*/ |
|
75 if(PythonState & PYTHON_MUSTWAKEUP){ |
|
76 /* WakeUp python thread */ |
|
77 UnBlockPythonCommands(); |
|
78 } |
|
79 UnLockPython(); |
|
80 } |
|
81 } |
|
82 /** |
|
83 * Called by the PLC, each time a python_eval |
|
84 * FB instance is executed |
|
85 */ |
|
86 void __PythonEvalFB(PYTHON_EVAL* data__) |
|
87 { |
|
88 /* python thread is not in ? */ |
|
89 if( PythonState & PYTHON_LOCKED_BY_PLC){ |
|
90 /* Rising edge on TRIG */ |
|
91 if(data__->TRIG && !data__->TRIGM1 && |
|
92 /* and not already being processed */ |
|
93 data__->STATE == PYTHON_FB_FREE) |
|
94 { |
|
95 /* Get a new line */ |
|
96 Current_PLC_EvalFB = (Current_PLC_EvalFB + 1) %% %(python_eval_fb_count)d; |
|
97 /* Enter the block in the fifo |
|
98 /* Don't have to check if fifo cell is free |
|
99 * as fifo size == FB count, and a FB cannot |
|
100 * be requested twice */ |
|
101 EvalFBs[Current_PLC_EvalFB] = data__; |
|
102 /* copy CODE in variable into BUFFER local*/ |
|
103 data__->BUFFER = data__->CODE; |
|
104 /* Set ACK pin to low so that we can set a rising edge on result */ |
|
105 data__->ACK = 0; |
|
106 /* Mark FB busy */ |
|
107 data__->STATE = PYTHON_FB_REQUESTED; |
|
108 /* Have to wakeup python thread in case he was asleep */ |
|
109 PythonState |= PYTHON_MUSTWAKEUP; |
|
110 //printf("__PythonEvalFB push %%*s\n",data__->BUFFER.len, data__->BUFFER.body); |
|
111 }else if(data__->STATE == PYTHON_FB_ANSWERED){ |
|
112 data__->RESULT = data__->BUFFER; |
|
113 data__->ACK = 1; |
|
114 data__->STATE = PYTHON_FB_FREE; |
|
115 //printf("__PythonEvalFB pop %%*s\n",data__->BUFFER.len, data__->BUFFER.body); |
|
116 } |
|
117 /* retain value for trig |
|
118 * do this only when PYTHON_LOCKED_BY_PLC |
|
119 * to avoid missed rising edge due to asynchronism */ |
|
120 data__->TRIGM1 = data__->TRIG; |
|
121 } |
|
122 } |
|
123 |
|
124 char* PythonIterator(char* result) |
|
125 { |
|
126 char* next_command; |
|
127 PYTHON_EVAL* data__; |
|
128 //printf("PythonIterator result %%s\n", result); |
|
129 /* take python mutex to prevent changing PLC data while PLC running */ |
|
130 LockPython(); |
|
131 /* Get current FB */ |
|
132 data__ = EvalFBs[Current_Python_EvalFB]; |
|
133 if(data__ && /* may be null at first run */ |
|
134 data__->STATE == PYTHON_FB_PROCESSING){ /* some answer awaited*/ |
|
135 /* If result not None */ |
|
136 if(result){ |
|
137 /* Get results len */ |
|
138 data__->BUFFER.len = strlen(result); |
|
139 /* prevent results overrun */ |
|
140 if(data__->BUFFER.len > STR_MAX_LEN) |
|
141 { |
|
142 data__->BUFFER.len = STR_MAX_LEN; |
|
143 /* TODO : signal error */ |
|
144 } |
|
145 /* Copy results to buffer */ |
|
146 strncpy(data__->BUFFER.body, result, data__->BUFFER.len); |
|
147 }else{ |
|
148 data__->BUFFER.len = 0; |
|
149 } |
|
150 /* remove block from fifo*/ |
|
151 EvalFBs[Current_Python_EvalFB] = NULL; |
|
152 /* Mark block as answered */ |
|
153 data__->STATE = PYTHON_FB_ANSWERED; |
|
154 /* Get a new line */ |
|
155 Current_Python_EvalFB = (Current_Python_EvalFB + 1) %% %(python_eval_fb_count)d; |
|
156 } |
|
157 /* while next slot is empty */ |
|
158 while(((data__ = EvalFBs[Current_Python_EvalFB]) == NULL) || |
|
159 /* or doesn't contain command */ |
|
160 data__->STATE != PYTHON_FB_REQUESTED) |
|
161 { |
|
162 UnLockPython(); |
|
163 /* wait next FB to eval */ |
|
164 WaitPythonCommands(); |
|
165 LockPython(); |
|
166 } |
|
167 /* Mark block as processing */ |
|
168 data__->STATE = PYTHON_FB_PROCESSING; |
|
169 //printf("PythonIterator\n"); |
|
170 /* make BUFFER a null terminated string */ |
|
171 data__->BUFFER.body[data__->BUFFER.len] = 0; |
|
172 /* next command is BUFFER */ |
|
173 next_command = data__->BUFFER.body; |
|
174 /* free python mutex */ |
|
175 UnLockPython(); |
|
176 /* return the next command to eval */ |
|
177 return next_command; |
|
178 } |
|
179 |