Linux runtime: overrun detection for real-time timers and for plc execution.
If real-time timer wakes-up PLC thread too late (10% over period), then
warning is logged.
If PLC code (IO retreive, execution, IO publish) takes longer than requested
PLC execution cycle, then warning is logged, and CPU hoogging is mitigated
by delaying next PLC execution a few cylces more until having at least
1ms minimal idle time.
<?xml version='1.0' encoding='utf-8'?>
<PyFile xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<variables>
<variable name="AlarmNotify" type="HMI_INT"/>
<variable name="SendAlarm" type="HMI_INT" onchange="TriggerAlarm"/>
<variable name="AlarmText" type="HMI_STRING" initial="'POS'"/>
<variable name="AlarmStatus" type="HMI_STRING" initial="'alarm'"/>
</variables>
<globals>
<xhtml:p><![CDATA[
from twisted.web.resource import Resource
import json, time, random, collections
Alarms = []
AlarmIndex = {}
lastid = 0
def TriggerAlarm(changed_var_name):
global Alarms, lastid
new_entry = [time.time(), PLCGlobals.AlarmText, PLCGlobals.AlarmStatus, lastid]
Alarms.append(new_entry)
AlarmIndex[lastid] = new_entry
lastid = lastid + 1
PLCGlobals.AlarmNotify = random.randint(0, 4294967296)
class AlarmJsonResource(Resource):
def render_GET(self, request):
return ''
def render_POST(self, request):
newstr = request.content.getvalue()
newdata = json.loads(newstr)
args = newdata[u'args']
range_feedback = newdata[u'range']
slider_position = newdata[u'position']
visible = newdata[u'visible']
extra = newdata[u'extra']
options = newdata[u'options']
if len(options) == 1 :
action, = options
if action == "action_reset":
del Alarms[:]
AlarmIndex.clear()
elif len(options) == 2 :
action, alarmid = options
if action == "onClick[acknowledge]":
AlarmIndex[int(alarmid)][2] = "ack"
answer = self.renderTable(range_feedback, slider_position, visible, extra)
janswer = json.dumps(answer)
return janswer
def renderTable(self, old_range, old_position, visible, extra):
if len(extra) > 0 and extra[0] != "":
fAlarms = [alrm for alrm in Alarms if alrm[1].find(extra[0])!=-1]
else:
fAlarms = Alarms[:]
fAlarms.reverse()
new_range = len(fAlarms)
delta = new_range - visible
new_position = 0 if delta <= 0 else delta if old_position > delta else old_position
new_visible = new_range if delta <= 0 else visible
visible_alarms = []
for ts, text, status, alarmid in fAlarms[new_position:new_position + new_visible]:
visible_alarms.append({
"time": time.ctime(ts),
"text": text, # TODO translate text
"status": status,
"alarmid": alarmid
})
return new_range, new_position, visible_alarms
]]></xhtml:p>
</globals>
<init>
<xhtml:p><![CDATA[
]]></xhtml:p>
</init>
<cleanup>
<xhtml:p><![CDATA[
]]></xhtml:p>
</cleanup>
<start>
<xhtml:p><![CDATA[
AddPathToSVGHMIServers("alarms", AlarmJsonResource)
]]></xhtml:p>
</start>
<stop>
<xhtml:p><![CDATA[
]]></xhtml:p>
</stop>
</PyFile>