22 # along with this program; if not, write to the Free Software |
22 # along with this program; if not, write to the Free Software |
23 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
23 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
24 |
24 |
25 import datetime |
25 import datetime |
26 |
26 |
27 #------------------------------------------------------------------------------- |
27 |
|
28 # ------------------------------------------------------------------------------- |
28 # Date and Time conversion function |
29 # Date and Time conversion function |
29 #------------------------------------------------------------------------------- |
30 # ------------------------------------------------------------------------------- |
30 |
31 |
31 SECOND = 1000000 # Number of microseconds in one second |
32 SECOND = 1000000 # Number of microseconds in one second |
32 MINUTE = 60 * SECOND # Number of microseconds in one minute |
33 MINUTE = 60 * SECOND # Number of microseconds in one minute |
33 HOUR = 60 * MINUTE # Number of microseconds in one hour |
34 HOUR = 60 * MINUTE # Number of microseconds in one hour |
34 DAY = 24 * HOUR # Number of microseconds in one day |
35 DAY = 24 * HOUR # Number of microseconds in one day |
35 |
36 |
36 # Date corresponding to Epoch (1970 January the first) |
37 # Date corresponding to Epoch (1970 January the first) |
37 DATE_ORIGIN = datetime.datetime(1970, 1, 1) |
38 DATE_ORIGIN = datetime.datetime(1970, 1, 1) |
38 |
39 |
|
40 |
39 def get_microseconds(value): |
41 def get_microseconds(value): |
40 """ |
42 """ |
41 Function converting time duration expressed in day, second and microseconds |
43 Function converting time duration expressed in day, second and microseconds |
42 into one expressed in microseconds |
44 into one expressed in microseconds |
43 @param value: Time duration to convert |
45 @param value: Time duration to convert |
44 @return: Time duration expressed in microsecond |
46 @return: Time duration expressed in microsecond |
45 """ |
47 """ |
46 return float(value.days * DAY + \ |
48 return float(value.days * DAY + |
47 value.seconds * SECOND + \ |
49 value.seconds * SECOND + |
48 value.microseconds) |
50 value.microseconds) |
49 return |
51 return |
|
52 |
50 |
53 |
51 def generate_time(value): |
54 def generate_time(value): |
52 """ |
55 """ |
53 Function converting time duration expressed in day, second and microseconds |
56 Function converting time duration expressed in day, second and microseconds |
54 into a IEC 61131 TIME literal |
57 into a IEC 61131 TIME literal |
55 @param value: Time duration to convert |
58 @param value: Time duration to convert |
56 @return: IEC 61131 TIME literal |
59 @return: IEC 61131 TIME literal |
57 """ |
60 """ |
58 microseconds = get_microseconds(value) |
61 microseconds = get_microseconds(value) |
59 |
62 |
60 # Get absolute microseconds value and save if it was negative |
63 # Get absolute microseconds value and save if it was negative |
61 negative = microseconds < 0 |
64 negative = microseconds < 0 |
62 microseconds = abs(microseconds) |
65 microseconds = abs(microseconds) |
63 |
66 |
64 # TIME literal prefix |
67 # TIME literal prefix |
65 data = "T#" |
68 data = "T#" |
66 if negative: |
69 if negative: |
67 data += "-" |
70 data += "-" |
68 |
71 |
69 # In TIME literal format, it isn't mandatory to indicate null values |
72 # In TIME literal format, it isn't mandatory to indicate null values |
70 # if no greater non-null values are available. This variable is used to |
73 # if no greater non-null values are available. This variable is used to |
71 # inhibit formatting until a non-null value is found |
74 # inhibit formatting until a non-null value is found |
72 not_null = False |
75 not_null = False |
73 |
76 |
74 for val, format in [ |
77 for val, format in [ |
75 (int(microseconds) / DAY, "%dd"), # Days |
78 (int(microseconds) / DAY, "%dd"), # Days |
76 ((int(microseconds) % DAY) / HOUR, "%dh"), # Hours |
79 ((int(microseconds) % DAY) / HOUR, "%dh"), # Hours |
77 ((int(microseconds) % HOUR) / MINUTE, "%dm"), # Minutes |
80 ((int(microseconds) % HOUR) / MINUTE, "%dm"), # Minutes |
78 ((int(microseconds) % MINUTE) / SECOND, "%ds")]: # Seconds |
81 ((int(microseconds) % MINUTE) / SECOND, "%ds")]: # Seconds |
79 |
82 |
80 # Add value to TIME literal if value is non-null or another non-null |
83 # Add value to TIME literal if value is non-null or another non-null |
81 # value have already be found |
84 # value have already be found |
82 if val > 0 or not_null: |
85 if val > 0 or not_null: |
83 data += format % val |
86 data += format % val |
84 |
87 |
85 # Update non-null variable |
88 # Update non-null variable |
86 not_null = True |
89 not_null = True |
87 |
90 |
88 # In any case microseconds have to be added to TIME literal |
91 # In any case microseconds have to be added to TIME literal |
89 data += "%gms" % (microseconds % SECOND / 1000.) |
92 data += "%gms" % (microseconds % SECOND / 1000.) |
90 |
93 |
91 return data |
94 return data |
92 |
95 |
|
96 |
93 def generate_date(value): |
97 def generate_date(value): |
94 """ |
98 """ |
95 Function converting time duration expressed in day, second and microseconds |
99 Function converting time duration expressed in day, second and microseconds |
96 into a IEC 61131 DATE literal |
100 into a IEC 61131 DATE literal |
97 @param value: Time duration to convert |
101 @param value: Time duration to convert |
98 @return: IEC 61131 DATE literal |
102 @return: IEC 61131 DATE literal |
99 """ |
103 """ |
100 return (DATE_ORIGIN + value).strftime("DATE#%Y-%m-%d") |
104 return (DATE_ORIGIN + value).strftime("DATE#%Y-%m-%d") |
101 |
105 |
|
106 |
102 def generate_datetime(value): |
107 def generate_datetime(value): |
103 """ |
108 """ |
104 Function converting time duration expressed in day, second and microseconds |
109 Function converting time duration expressed in day, second and microseconds |
105 into a IEC 61131 DATE_AND_TIME literal |
110 into a IEC 61131 DATE_AND_TIME literal |
106 @param value: Time duration to convert |
111 @param value: Time duration to convert |
107 @return: IEC 61131 DATE_AND_TIME literal |
112 @return: IEC 61131 DATE_AND_TIME literal |
108 """ |
113 """ |
109 return (DATE_ORIGIN + value).strftime("DT#%Y-%m-%d-%H:%M:%S.%f") |
114 return (DATE_ORIGIN + value).strftime("DT#%Y-%m-%d-%H:%M:%S.%f") |
110 |
115 |
|
116 |
111 def generate_timeofday(value): |
117 def generate_timeofday(value): |
112 """ |
118 """ |
113 Function converting time duration expressed in day, second and microseconds |
119 Function converting time duration expressed in day, second and microseconds |
114 into a IEC 61131 TIME_OF_DAY literal |
120 into a IEC 61131 TIME_OF_DAY literal |
115 @param value: Time duration to convert |
121 @param value: Time duration to convert |
116 @return: IEC 61131 TIME_OF_DAY literal |
122 @return: IEC 61131 TIME_OF_DAY literal |
117 """ |
123 """ |
118 microseconds = get_microseconds(value) |
124 microseconds = get_microseconds(value) |
119 |
125 |
120 # TIME_OF_DAY literal prefix |
126 # TIME_OF_DAY literal prefix |
121 data = "TOD#" |
127 data = "TOD#" |
122 |
128 |
123 for val, format in [ |
129 for val, format in [ |
124 (int(microseconds) / HOUR, "%2.2d:"), # Hours |
130 (int(microseconds) / HOUR, "%2.2d:"), # Hours |
125 ((int(microseconds) % HOUR) / MINUTE, "%2.2d:"), # Minutes |
131 ((int(microseconds) % HOUR) / MINUTE, "%2.2d:"), # Minutes |
126 ((int(microseconds) % MINUTE) / SECOND, "%2.2d."), # Seconds |
132 ((int(microseconds) % MINUTE) / SECOND, "%2.2d."), # Seconds |
127 (microseconds % SECOND, "%6.6d")]: # Microseconds |
133 (microseconds % SECOND, "%6.6d")]: # Microseconds |
128 |
134 |
129 # Add value to TIME_OF_DAY literal |
135 # Add value to TIME_OF_DAY literal |
130 data += format % val |
136 data += format % val |
131 |
137 |
132 return data |
138 return data |
133 |
139 |
134 # Dictionary of translation functions from value send by debugger to IEC |
140 |
|
141 # Dictionary of translation functions from value send by debugger to IEC |
135 # literal stored by type |
142 # literal stored by type |
136 TYPE_TRANSLATOR = { |
143 TYPE_TRANSLATOR = { |
137 "TIME": generate_time, |
144 "TIME": generate_time, |
138 "DATE": generate_date, |
145 "DATE": generate_date, |
139 "DT": generate_datetime, |
146 "DT": generate_datetime, |
141 "STRING": lambda v: "'%s'" % v, |
148 "STRING": lambda v: "'%s'" % v, |
142 "WSTRING": lambda v: '"%s"' % v, |
149 "WSTRING": lambda v: '"%s"' % v, |
143 "REAL": lambda v: "%.6g" % v, |
150 "REAL": lambda v: "%.6g" % v, |
144 "LREAL": lambda v: "%.6g" % v} |
151 "LREAL": lambda v: "%.6g" % v} |
145 |
152 |
146 #------------------------------------------------------------------------------- |
153 |
|
154 # ------------------------------------------------------------------------------- |
147 # Debug Data Consumer Class |
155 # Debug Data Consumer Class |
148 #------------------------------------------------------------------------------- |
156 # ------------------------------------------------------------------------------- |
149 |
157 |
150 """ |
|
151 Class that implements an element that consumes debug values |
|
152 Value update can be inhibited during the time the associated Debug Viewer is |
|
153 refreshing |
|
154 """ |
|
155 |
158 |
156 class DebugDataConsumer: |
159 class DebugDataConsumer: |
157 |
160 """ |
|
161 Class that implements an element that consumes debug values |
|
162 Value update can be inhibited during the time the associated Debug Viewer is |
|
163 refreshing |
|
164 """ |
|
165 |
158 def __init__(self): |
166 def __init__(self): |
159 """ |
167 """ |
160 Constructor |
168 Constructor |
161 """ |
169 """ |
162 # Debug value and forced flag |
170 # Debug value and forced flag |
163 self.Value = None |
171 self.Value = None |
164 self.Forced = False |
172 self.Forced = False |
165 |
173 |
166 # Store debug value and forced flag when value update is inhibited |
174 # Store debug value and forced flag when value update is inhibited |
167 self.LastValue = None |
175 self.LastValue = None |
168 self.LastForced = False |
176 self.LastForced = False |
169 |
177 |
170 # Value IEC data type |
178 # Value IEC data type |
171 self.DataType = None |
179 self.DataType = None |
172 |
180 |
173 # Flag that value update is inhibited |
181 # Flag that value update is inhibited |
174 self.Inhibited = False |
182 self.Inhibited = False |
175 |
183 |
176 def Inhibit(self, inhibit): |
184 def Inhibit(self, inhibit): |
177 """ |
185 """ |
178 Set flag to inhibit or activate value update |
186 Set flag to inhibit or activate value update |
179 @param inhibit: Inhibit flag |
187 @param inhibit: Inhibit flag |
180 """ |
188 """ |
181 # Save inhibit flag |
189 # Save inhibit flag |
182 self.Inhibited = inhibit |
190 self.Inhibited = inhibit |
183 |
191 |
184 # When reactivated update value and forced flag with stored values |
192 # When reactivated update value and forced flag with stored values |
185 if not inhibit and self.LastValue is not None: |
193 if not inhibit and self.LastValue is not None: |
186 self.SetForced(self.LastForced) |
194 self.SetForced(self.LastForced) |
187 self.SetValue(self.LastValue) |
195 self.SetValue(self.LastValue) |
188 |
196 |
189 # Reset stored values |
197 # Reset stored values |
190 self.LastValue = None |
198 self.LastValue = None |
191 self.LastForced = False |
199 self.LastForced = False |
192 |
200 |
193 def SetDataType(self, data_type): |
201 def SetDataType(self, data_type): |
194 """ |
202 """ |
195 Set value IEC data type |
203 Set value IEC data type |
196 @param data_type: Value IEC data type |
204 @param data_type: Value IEC data type |
197 """ |
205 """ |
198 self.DataType = data_type |
206 self.DataType = data_type |
199 |
207 |
200 def NewValues(self, tick, values, raw="BOOL"): |
208 def NewValues(self, tick, values, raw="BOOL"): |
201 """ |
209 """ |
202 Function called by debug thread when a new debug value is available |
210 Function called by debug thread when a new debug value is available |
203 @param tick: PLC tick when value was captured |
211 @param tick: PLC tick when value was captured |
204 @param value: Value captured |
212 @param value: Value captured |
205 @param forced: Forced flag, True if value is forced (default: False) |
213 @param forced: Forced flag, True if value is forced (default: False) |
206 @param raw: Data type of values not translated (default: 'BOOL') |
214 @param raw: Data type of values not translated (default: 'BOOL') |
207 """ |
215 """ |
208 value, forced = values |
216 value, forced = values |
209 |
217 |
210 # Translate value to IEC literal |
218 # Translate value to IEC literal |
211 if self.DataType != raw: |
219 if self.DataType != raw: |
212 value = TYPE_TRANSLATOR.get(self.DataType, str)(value) |
220 value = TYPE_TRANSLATOR.get(self.DataType, str)(value) |
213 |
221 |
214 # Store value and forced flag when value update is inhibited |
222 # Store value and forced flag when value update is inhibited |
215 if self.Inhibited: |
223 if self.Inhibited: |
216 self.LastValue = value |
224 self.LastValue = value |
217 self.LastForced = forced |
225 self.LastForced = forced |
218 |
226 |
219 # Update value and forced flag in any other case |
227 # Update value and forced flag in any other case |
220 else: |
228 else: |
221 self.SetForced(forced) |
229 self.SetForced(forced) |
222 self.SetValue(value) |
230 self.SetValue(value) |
223 |
231 |
224 def SetValue(self, value): |
232 def SetValue(self, value): |
225 """ |
233 """ |
226 Update value. |
234 Update value. |
227 May be overridden by inherited classes |
235 May be overridden by inherited classes |
228 @param value: New value |
236 @param value: New value |
229 """ |
237 """ |
230 self.Value = value |
238 self.Value = value |
231 |
239 |
232 def GetValue(self): |
240 def GetValue(self): |
233 """ |
241 """ |
234 Return current value |
242 Return current value |
235 @return: Current value |
243 @return: Current value |
236 """ |
244 """ |
237 return self.Value |
245 return self.Value |
238 |
246 |
239 def SetForced(self, forced): |
247 def SetForced(self, forced): |
240 """ |
248 """ |
241 Update Forced flag. |
249 Update Forced flag. |
242 May be overridden by inherited classes |
250 May be overridden by inherited classes |
243 @param forced: New forced flag |
251 @param forced: New forced flag |
244 """ |
252 """ |
245 self.Forced = forced |
253 self.Forced = forced |
246 |
254 |
247 def IsForced(self): |
255 def IsForced(self): |
248 """ |
256 """ |
249 Indicate if current value is forced |
257 Indicate if current value is forced |
250 @return: Current forced flag |
258 @return: Current forced flag |
251 """ |
259 """ |