There is a bug in the current version where alarms can get a wrong alarm prioity after a GW restart.
I'm running a script disabling all alarms and then enabling them again to fix the bug and have the GW assign the correct alarm priority to the alarms.
I don't want to disable all alarms at once and then enable them all again at once. I want to do it in chunks and give the GW some time between the operations thus the time.sleep().
You want to use the techniques described in this topic:
(And follow the links in that topic to related discussions.)
Always keep in mind that the only safe thing to do with a Java Swing object in a background thread is to carry it around, to pass back to a short foreground function.
Thank you Phil, almost got it working now with your later.py script.
Only thing I'm missing is the ability to stop the thread?
At the moment i have a window with a 'is_running' bool custom property. I can tooogle it with a start and stop button, but can I get/read it in the thread and make the thread end if it's false?
This is the code of the 'Start' button:
def gw_restart_alarm_prio_fix(text_area, is_running):
import time
path = '[SCADA]'
folders = []
res = system.tag.browse(path, filter={'tagType':'Folder'})
for r in res.getResults():
fp = str(r['fullPath'])
folders.append(fp)
ust_bloker_alarmer_list = []
for folder in folders:
res = system.tag.browse(folder, filter={})
for r in res.getResults():
fp = str(r['fullPath'])
if 'USTBlokerAlarmer' in fp and fp.split('/')[-1] != 'USTBlokerAlarmer' and 'Puls' not in fp:
ust_bloker_alarmer_list.append(fp)
anl_list = []
for ust in ust_bloker_alarmer_list:
anl = ust.split('_')[0].split(']')[-1]
if anl not in anl_list:
anl_list.append(anl)
delay = 10 # sek
text = ''
for anl in anl_list:
if is_running: # <= this check doens't seem to work
ust_list = []
for ust in ust_bloker_alarmer_list:
if ust.split('_')[0].split(']')[-1] == anl:
ust_list.append(ust)
text += ust_list[0].split('/')[0] + '\n'
project.later.assignLater(text_area, 'text' , text)
system.tag.writeBlocking(ust_list, [True]*len(ust_list))
time.sleep(delay)
system.tag.writeBlocking(ust_list, [False]*len(ust_list))
time.sleep(delay)
text += 'done'
project.later.assignLater(text_area, 'text' , text)
# start button
event.source.parent.run = True
event.source.parent.getComponent('TA1_Log').text = 'Running' + '\n'
text_area = event.source.parent.getComponent('TA1_Log')
is_running = event.source.parent.is_running
project.later.callAsync(gw_restart_alarm_prio_fix, text_area, is_running)
Your is_running is a function argument--it is snapshotted when you start the thread. To get an update, you will have to use of the ..Later() functions to run a short script in the UI to retrieve the current value and return it. The future from my function will be completed with that value for you to .get().
def gw_restart_alarm_prio_fix(text_area, root_container):
import time
path = '[SCADA]'
folders = []
res = system.tag.browse(path, filter={'tagType':'Folder'})
for r in res.getResults():
fp = str(r['fullPath'])
folders.append(fp)
ust_bloker_alarmer_list = []
for folder in folders:
res = system.tag.browse(folder, filter={})
for r in res.getResults():
fp = str(r['fullPath'])
if 'USTBlokerAlarmer' in fp and fp.split('/')[-1] != 'USTBlokerAlarmer' and 'Puls' not in fp:
ust_bloker_alarmer_list.append(fp)
anl_list = []
for ust in ust_bloker_alarmer_list:
anl = ust.split('_')[0].split(']')[-1]
if anl not in anl_list:
anl_list.append(anl)
def get_is_running():
return root_container.is_running
delay = 10 # sek
text = ''
for anl in anl_list:
#if is_running:
if project.later.callLater(get_is_running, None).get():
ust_list = []
for ust in ust_bloker_alarmer_list:
if ust.split('_')[0].split(']')[-1] == anl:
ust_list.append(ust)
text += ust_list[0].split('/')[0] + '\n'
project.later.assignLater(text_area, 'text' , text)
system.tag.writeBlocking(ust_list, [True]*len(ust_list))
time.sleep(delay)
system.tag.writeBlocking(ust_list, [False]*len(ust_list))
time.sleep(delay)
text += 'done'
project.later.assignLater(text_area, 'text' , text)
# start button
event.source.parent.is_running = True
text_area = event.source.parent.getComponent('TA1_Log')
text_area.text = 'Running' + '\n'
root_containter = event.source.parent.getComponent('Root Container')
project.later.callAsync(gw_restart_alarm_prio_fix, text_area, root_containter)
You should also get rid import time, and any other use of the jython standard library that doesn't have java alternative. Use the various sleep methods of java.lang.Thread instead of time.sleep().
Ok so I've been tinkering a bit with and it seems to be working now.
For some reason to get values in the async function from passed UI components you need to make a small 'get' function and call that whenever you want the value.
Simply calling <UI_component.value> ie. <root_container.is_running> doesn't work.
This is the working code in the start button :
def gw_restart_alarm_prio_fix(text_area, root_container):
import time
path = '[SCADA]'
folders = []
res = system.tag.browse(path, filter={'tagType':'Folder'})
for r in res.getResults():
fp = str(r['fullPath'])
folders.append(fp)
ust_bloker_alarmer_list = []
for folder in folders:
res = system.tag.browse(folder, filter={})
for r in res.getResults():
fp = str(r['fullPath'])
if 'USTBlokerAlarmer' in fp and fp.split('/')[-1] != 'USTBlokerAlarmer' and 'Puls' not in fp:
ust_bloker_alarmer_list.append(fp)
anl_list = []
for ust in ust_bloker_alarmer_list:
anl = ust.split('_')[0].split(']')[-1]
if anl not in anl_list:
anl_list.append(anl)
def get_is_running():
return root_container.is_running
delay = 10 # sek
text = ''
for anl in anl_list:
if get_is_running():
ust_list = []
for ust in ust_bloker_alarmer_list:
if ust.split('_')[0].split(']')[-1] == anl:
ust_list.append(ust)
text += ust_list[0].split('/')[0] + '\n'
project.later.assignLater(text_area, 'text' , text)
system.tag.writeBlocking(ust_list, [True]*len(ust_list))
time.sleep(delay)
system.tag.writeBlocking(ust_list, [False]*len(ust_list))
time.sleep(delay)
text += 'done'
project.later.assignLater(text_area, 'text' , text)
# start button
event.source.parent.is_running = True
text_area = event.source.parent.getComponent('TA1_Log')
text_area.text = 'Running' + '\n'
root_containter = event.source.parent
project.later.callAsync(gw_restart_alarm_prio_fix, text_area, root_containter)
The stop button merely sets the root_containers custom property 'is_running' to false which ends the main for loop in the async function causing it to finish quickly.
I've yet to replace the import time and time.sleep() calls, but so far so good.
Well I'm no expert at this stuff. I wish I understood it better.
Consider this :
def my_func(ui_component):
value = ui_component.value # 1
def get_ui_component_value():
return ui_component.value
value = get_ui_component_value() # 2
To me there is no real difference between #1 and #2. They both ultimately get ui_component.value, #2 is just comming from a get function where #1 is read directly from the component. I don't understand how that can make a difference in the async function?
And earlier in the post you mentioned that passed parameters to the async function could be 'snapshotted' and thus unchanged in the async function - why doesn't that happen with the 'root_container' and 'text_area' too?
You don't need to answer all this, I guess I'm just venting my confusion about asynchronous programming.
There isn't a difference in that example. You aren't delegating to a "Later" function that completes a future for you. You absolutely need the later.callLater(...).get() to make the property retrieval happen in the foreground where it is safe.
Calling system.util.invokeLater() without wrapping the callable in a mechanism for a completable future simply gets you a None in value without any wait for the foreground. system.util.invokeLater() doesn't return a value.
Go look at later.callLater() in my later.py script.
I've been testing some more with your logger suggestion and it turns out that the error was in this part
...
def get_is_running():
return root_container.is_running
delay = 10 # sek
text = ''
for anl in anl_list:
#if is_running:
if project.later.callLater(get_is_running, None).get():
ust_list = []
...
Where "None" is passed as an argument to a function that takes no arguments.
After fixing this with "if project.later.callLater(get_is_running).get():" it works.
Here is the working code
def gw_restart_alarm_prio_fix(root_container, text_area, delay):
import time
path = '[SCADA]'
folders = []
res = system.tag.browse(path, filter={'tagType':'Folder'})
for r in res.getResults():
fp = str(r['fullPath'])
folders.append(fp)
ust_bloker_alarmer_list = []
for folder in folders:
res = system.tag.browse(folder, filter={})
for r in res.getResults():
fp = str(r['fullPath'])
if 'Puls' in fp:
ust_bloker_alarmer_list.append(fp)
anl_list = []
for ust in ust_bloker_alarmer_list:
anl = ust.split('_')[0].split(']')[-1]
if anl not in anl_list:
anl_list.append(anl)
def get_is_running():
return root_container.is_running
def get_text_area_text():
return text_area.text
def get_delay():
return delay.intValue
text = project.later.callLater(get_text_area_text).get()
for anl in anl_list:
if project.later.callLater(get_is_running).get():
ust_list = []
for ust in ust_bloker_alarmer_list:
if ust.split('_')[0].split(']')[-1] == anl:
ust_list.append(ust)
text = ust_list[0].split('/')[0] + '\n' + text
project.later.assignLater(text_area, 'text' , text)
system.tag.writeBlocking(ust_list, [True]*len(ust_list))
time.sleep(project.later.callLater(get_delay).get())
system.tag.writeBlocking(ust_list, [False]*len(ust_list))
time.sleep(project.later.callLater(get_delay).get())
text = 'Done' + '\n' + project.later.callLater(get_text_area_text).get()
project.later.assignLater(text_area, 'text' , text)
# start button
event.source.parent.is_running = True
text_area = event.source.parent.getComponent('TA1_Log')
text_area.text = 'Start'
root_containter = event.source.parent
delay = event.source.parent.getComponent('NTF1_Delay')
project.later.callAsync(gw_restart_alarm_prio_fix, root_containter, text_area, delay)
It's good that it runs, but it seems you haven't taken the advice to split the bulk into a project library script. Use def something directly in an event script may work in this specific case, but is hazardous in many places.
Ideally, even the last six lines would be in another project library script function. Make your events one-liners that delegate to the library for best long-term maintainability.