From 1d05a67a89480db30526d0702b4dc635a22f6554 Mon Sep 17 00:00:00 2001 From: anwinged Date: Mon, 7 May 2012 08:11:56 +0000 Subject: [PATCH] model states ench --- forms.py | 9 ++- opal.py | 152 +++++++++++++++++++++++++++------------- server.py | 16 ++++- share/model-cancel.png | Bin 0 -> 727 bytes share/model-no-exec.png | Bin 0 -> 518 bytes share/model-stop.png | Bin 0 -> 560 bytes tasks/testt.py | 3 +- 7 files changed, 126 insertions(+), 54 deletions(-) create mode 100644 share/model-cancel.png create mode 100644 share/model-no-exec.png create mode 100644 share/model-stop.png diff --git a/forms.py b/forms.py index 91788d3..d33f9ed 100644 --- a/forms.py +++ b/forms.py @@ -18,7 +18,9 @@ ID_ADD_MODEL_SELECTED = wx.NewId() ID_DUPLICATE_MODEL = wx.NewId() ID_DUPLICATE_TREE = wx.NewId() ID_DELETE_MODEL = wx.NewId() + ID_PROCESS_MODEL = wx.NewId() +ID_STOP_MODEL = wx.NewId() ID_SHOW_RESULT = wx.NewId() @@ -152,6 +154,8 @@ class MainFrame(wx.Frame): icons.mready = ilist.Add(wx.Bitmap('share/model-ready.png')) icons.mrun = ilist.Add(wx.Bitmap('share/model-run.png')) icons.mcomplete = ilist.Add(wx.Bitmap('share/model-complete.png')) + icons.mstopped = ilist.Add(wx.Bitmap('share/model-stop.png')) + icons.mnoexec = ilist.Add(wx.Bitmap('share/model-no-exec.png')) icons.porg = ilist.Add(wx.Bitmap('share/plot-org.png')) icons.pline = ilist.Add(wx.Bitmap('share/plot-line.png')) @@ -182,6 +186,7 @@ class MainFrame(wx.Frame): menu = wx.Menu() menu.Append(ID_PROCESS_MODEL, 'Process\tF5') + menu.Append(ID_STOP_MODEL, 'Stop\tF6') #menu.AppendSeparator() menubar.Append(menu, '&Run') @@ -237,7 +242,9 @@ class MainFrame(wx.Frame): 'Delete selected model') tb1.AddSeparator() tb1.AddSimpleTool(ID_PROCESS_MODEL, "model-go", wx.Bitmap('share/model-go.png'), - 'Start process selected model') + 'Start processing of selected models') + tb1.AddSimpleTool(ID_STOP_MODEL, "model-stop", wx.Bitmap('share/model-cancel.png'), + 'Stop processing of selected models') tb1.AddSeparator() tb1.AddSimpleTool(ID_SHOW_PLOT, "plot-quick", wx.Bitmap('share/plot-line.png'), 'Show quick plot for selected model') diff --git a/opal.py b/opal.py index cdac4a6..0722493 100644 --- a/opal.py +++ b/opal.py @@ -27,21 +27,43 @@ import json import zlib from pprint import pprint +# состояния модели, унаследованные от состояния задачи +MODEL_READY = server.JOB_READY +MODEL_RUNNING = server.JOB_RUNNING +MODEL_STOPPED = server.JOB_STOPPED +MODEL_COMPLETED = server.JOB_COMPLETED +# собственные состояния модели +MODEL_NO_EXEC = 101 + +# -------------------------------------------------------------------------- +# Данные о пользовательской модели +# -------------------------------------------------------------------------- + class ModelData: def __init__(self, server, model, parent_data = None): # если мы создаем новый набор данных из описания модели if isinstance(model, task.DataDescription): self.mdef = task.DataDefinition(model, parent_data) - self.jid = server.CreateJob() if model.IsExecutable() else None + self.jid = server.CreateJob() if model.IsExecutable() else None # если мы создаем набор данных из другого набора данных elif isinstance(model, ModelData): self.mdef = model.mdef.Copy() - self.jid = server.CreateJob() if model.jid else None + self.jid = server.CreateJob() if model.jid else None else: self.mdef = None - self.jid = None + self.jid = None - self.res = None + assert self.mdef + + self.res = None # результаты выполнения работы + if self.jid: + self.state = MODEL_READY # состояние модели + else: + self.state = MODEL_NO_EXEC # состояние модели + +# -------------------------------------------------------------------------- +# Данные о линии в графике +# -------------------------------------------------------------------------- LINE_CURVE = 1 LINE_MARKER = 2 @@ -96,6 +118,10 @@ class LineData: assert data.res # если результата нет, то а-та-та return data.res.Zip(*self.columns) +# -------------------------------------------------------------------------- +# Ошибки доступа к элементам в контейнерах +# -------------------------------------------------------------------------- + class ItemError(Exception): pass @@ -156,8 +182,11 @@ class MainFrame(forms.MainFrame): id = forms.ID_DUPLICATE_TREE) self.Bind(wx.EVT_MENU, self.OnDeleteModel, id = forms.ID_DELETE_MODEL) + self.Bind(wx.EVT_MENU, self.OnModelProcess, id = forms.ID_PROCESS_MODEL) + self.Bind(wx.EVT_MENU, self.OnModelStop, + id = forms.ID_STOP_MODEL) self.Bind(wx.EVT_MENU, self.OnShowResult, id = forms.ID_SHOW_RESULT) @@ -214,19 +243,6 @@ class MainFrame(forms.MainFrame): состояние окружения, выводит информацию, подгружает результаты выполнения работ и др. """ - - def StateToStr(state): - if state == server.JOB_READY: - return 'Ready' - elif state == server.JOB_RUNNING: - return 'Running' - elif state == server.JOB_STOPPED: - return 'Stopped' - elif state == server.JOB_COMPLETED: - return 'Completed' - else: - return 'Unknown' - try: um = self.m_user_models cycle_count = 0 @@ -247,25 +263,29 @@ class MainFrame(forms.MainFrame): if not data: continue jid = data.jid - if jid != None and self.server.IsJobChanged(jid): + if jid and self.server.IsJobChanged(jid): + # таким образом, тут мы обрабатываем новое состояние + # работы (модели) + state, percent, comment = self.server.GetJobState(jid) - um.SetItemText(item, StateToStr(state), 1) + + data.state = state + self.SetModelState(item, data.state) + p = 'Unknown' if percent < 0 else '{:%}'.format(percent) um.SetItemText(item, p, 2) um.SetItemText(item, comment, 3) + print 'JID', jid, (state, percent, comment) + # завершающие действия по окончанию выполнения работы if state == server.JOB_COMPLETED: - # устанавливаем иконку для завершенной модели - um.SetItemImage(item, self.icons.mcomplete) # получаем результаты выполнения data.res = self.server.GetJobResult(jid) # если завершившаяся задача в данный момент выделена # то сразу же показываем этот результат if um.IsSelected(item): self.ShowQuickResult(data.res) - else: - um.SetItemImage(item, self.icons.mrun) finally: wx.MutexGuiLeave() pass @@ -396,7 +416,6 @@ class MainFrame(forms.MainFrame): except Exception, e: print 'Oops', type(e), e - def OnSaveProject(self, event): def WalkModels(item, dest): @@ -407,7 +426,8 @@ class MainFrame(forms.MainFrame): dest[title] = { 'model': mdef.DD.GetLabel(), 'data': mdef.params, - 'um': {} + 'um': {}, + 'state': data.state, } if data.res: dest[title]['result'] = data.res.DumpData() @@ -457,9 +477,9 @@ class MainFrame(forms.MainFrame): # pprint(data) dump = json.dumps(data, indent = 2) - with open('data.opl', 'wb') as f: - f.write(zlib.compress(dump, 9)) - # f.write(dump) + with open('data.opl', 'w') as f: + # f.write(zlib.compress(dump, 9)) + f.write(dump) wx.EndBusyCursor() @@ -495,6 +515,41 @@ class MainFrame(forms.MainFrame): # Добавление новых моделей + def SetModelState(self, item, state): + if state == MODEL_READY: + icon = self.icons.mready + text = 'Ready' + + elif state == MODEL_RUNNING: + icon = self.icons.mrun + text = 'Running' + + elif state == MODEL_COMPLETED: + icon = self.icons.mcomplete + text = 'Completed' + + elif state == MODEL_STOPPED: + icon = self.icons.mstopped + text = 'Stopped' + + else: + icon = self.icons.mnoexec + text = 'No executable' + + self.m_user_models.SetItemImage(item, icon) + self.m_user_models.SetItemText(item, text, 1) + + def AddModel(self, item, title, model_data): + """ + Добавляет модель к указанной, + устанавливает имя, данные, состояние, иконку + """ + um = self.m_user_models + item = um.AppendItem(item, title) + um.SetPyData(item, model_data) + self.SetModelState(item, model_data.state) + return item + def AddModelToRoot(self, model): """ Добавляет пользовательскую модель или спецификацию @@ -514,13 +569,11 @@ class MainFrame(forms.MainFrame): root = None for i, m in enumerate(ms): name = self.GenerateName(m.GetTitle()) - item = um.AppendItem(item, name) - um.SetItemImage(item, self.icons.mready) if not i: root = item data = ModelData(self.server, m, defparent) + item = self.AddModel(item, name, data) defparent = data.mdef - um.SetPyData(item, data) if root: um.Expand(root) um.SelectItem(item) @@ -543,10 +596,8 @@ class MainFrame(forms.MainFrame): # если новая модель может быть присоединена... if pmdef.DD == model.parent: name = self.GenerateName(model.GetTitle()) - child = um.AppendItem(item, name) new_data = ModelData(self.server, model, pmdef) - um.SetPyData(child, new_data) - um.SetItemImage(child, self.icons.mready) + child = self.AddModel(item, name, new_data) um.SetFocus() um.Expand(item) um.SelectItem(child) @@ -616,7 +667,11 @@ class MainFrame(forms.MainFrame): def OnParamChanged(self, event): def Walk(item): - um.SetItemImage(item, self.icons.mready) + data = um.GetPyData(item) + if data.state != MODEL_NO_EXEC: + data.state = MODEL_READY + self.SetModelState(item, data.state) + child, _ = um.GetFirstChild(item) while child.IsOk(): Walk(child) @@ -635,17 +690,7 @@ class MainFrame(forms.MainFrame): Walk(item) def OnTest(self, event): - - def Walk(item): - print um.GetItemText(item) - um.SetItemImage(item, self.icons.mready) - child, cookie = um.GetFirstChild(item) - while child.IsOk(): - Walk(child) - child = um.GetNextSibling(child) - - um = self.m_user_models - Walk(um.GetRootItem()) + pass # Получение данных выбранной модели @@ -678,7 +723,7 @@ class MainFrame(forms.MainFrame): new_data = ModelData(self.server, data) um.SetItemText(item_dst, self.GenerateName(title)) um.SetPyData(item_dst, new_data) - um.SetItemImage(item_dst, self.icons.mready) + self.SetModelState(item_dst, new_data.state) def OnDuplicate(self, event): """ @@ -731,7 +776,15 @@ class MainFrame(forms.MainFrame): um = self.m_user_models for i in um.GetSelections(): data = um.GetItemPyData(i) - self.server.LaunchJob(data.jid, data.mdef) + if data.jid: + self.server.LaunchJob(data.jid, data.mdef) + + def OnModelStop(self, event): + um = self.m_user_models + for i in um.GetSelections(): + data = um.GetItemPyData(i) + if data.jid: + self.server.StopJob(data.jid) # Функции управления таблицами и отчетами @@ -842,7 +895,8 @@ class MainFrame(forms.MainFrame): def ShowPlot(self, lines, plot_title = ''): if lines: p = PlotFrame(self, 'Plot', lines) - p.Show() + wx.FutureCall(20, p.Show) + # p.Show() def OnQuickShowPlot(self, event): lines = self.GetLines(LINE_CURVE) diff --git a/server.py b/server.py index 025c550..64b2494 100644 --- a/server.py +++ b/server.py @@ -219,16 +219,23 @@ class Worker(threading.Thread): proc.stdin.flush() # пока процесс не завершится (или его не прибьют) while proc.poll() == None: + # читаем и обрабатываем сообщение msg = proc.stdout.readline() self.ProcessMessage(job, msg) + + # сервер был остановлен, завершаем выполнение всех работ if not self.server.running: proc.kill() - raise KeyError + except Exception, e: + # любая нестандартная исключительная ситуация + # приводит к немедленному завершанию работы WriteToLog('Job loop failed: ' + str(e)) job.Finish(JOB_STOPPED) else: - job.Finish(JOB_COMPLETED, 1.0) + # только если работа уже не была остановлена + if job.state != JOB_STOPPED: + job.Finish(JOB_COMPLETED, 1.0) def ProcessMessage(self, job, msg): try: @@ -246,7 +253,10 @@ class Worker(threading.Thread): job.result = task.ResultData(data['result']) # произошла ошибка elif ans == 'error': + # произошла серьезная ошибка + # завршаем выполнение работы WriteToLog('Error! ' + msg) + raise RuntimeError, msg # недокументированный ответ приложения else: pass @@ -318,7 +328,7 @@ class Job: if self.proc and self.proc.poll() == None: WriteToLog('Try to kill') self.proc.kill() - self.ChangeState() + self.Finish(JOB_STOPPED) WriteToLog('Job killed') def Finish(self, state, percent = None): diff --git a/share/model-cancel.png b/share/model-cancel.png new file mode 100644 index 0000000000000000000000000000000000000000..33c876b23a8a3940476e354ffe81f9fe93ac9170 GIT binary patch literal 727 zcmV;|0x127P)S&XCR(-5a9UMJ?X+Da1x7myH-=OMF``hLXxS=>7G@dFfQu*#s*%bW zXB=i6^}W|QcY2Sp=)mROd+zyu@7#0G6{xYP|!6}_b(_u zpM(emNGU1GfxEtr$eA8EyY?XcU<_~^4bgz9u6Qq{n2hyZhWhpy8=0?w3Sf2iH9>+T zUPHL;7+|l%Xxa_Cc`wpq_xSILuBo|kygYKy%|ChM7DsNni`^}*XW4=C?&8V)`s*3* za~#LBjx&%BrN~x!@eIne{DL+NKSn6ps=uBobBm5+b99ja$(6DwJ!zUyk$PlLMSa1x z(Wm~L{VGyNaTd%%u3A|Qki^LTf((SAf&{VZu+pEXy|D-K;SC5nmw98v7Zy*){W<%2U(xl-Vhuph z5lTcfLUe>s$QP8)o`BlftiOH{Y2Tnx$`|MaRe-n?-*>&Ev3{s6dH|-OV6T>{9Y#lj zQgr_|^+H9rvkF@%Z`hcf|A3inVX%Mh<1NNC4#!*i4mZ@_?ubWWnjvaF>OO8#ua$X4 znU>2H%%tY=CRez2HMKCt?c%)!4e2CE=b=^vBUbetc}7ud#S*69zQ^<*G2iT_$HWji z&XxA9){t(JzW{s#@A9R=fz+o_7XR}aJqV2!{NENlu?P4qzyNNT8~9r=WRd^?002ov JPDHLkV1fmNR7L;* literal 0 HcmV?d00001 diff --git a/share/model-no-exec.png b/share/model-no-exec.png new file mode 100644 index 0000000000000000000000000000000000000000..0954aa403fd537365116613db7a24ca589e36a0c GIT binary patch literal 518 zcmV+h0{Q)kP))VBCd;yD)CR1&D>45G(|3MNtq`g!qi!`R8gl z7ewKD;LkbCeDf!qnfUJaJAJc!FtBY~jg?t7R+VKuVnbGs)n$#Jpk-O4<8Zs($TUqF z353sChTB}$rQam0P!&gEM3|Q272K-sIOeT|y#bP+W z6&SFn@eJ6#no6bUd_I3P00R~^-W_nl*zfnWUau(>3Q;5yk)1D;9(g*QPINdNWJeA) zs)2OEaU9z1c5nL*QwTMx!H^A%a5(&NaKPYYs8QS4;~pD@LV=!h@oMqyQ!p5$`Ft)l zY8Se**=&h>0Dw=_b^VVJLkLrRrsQ>@~07*qo IM6N<$f)Z!mx&QzG literal 0 HcmV?d00001 diff --git a/share/model-stop.png b/share/model-stop.png new file mode 100644 index 0000000000000000000000000000000000000000..1c500ba34e6e023796bf329bb335390d9649c3d6 GIT binary patch literal 560 zcmV-00?+-4P)gmOg-u*x8Gng`$XHRtZt! zYRvBJkLQkYi4aogz#-Y0@0`6ecSq%Uj{gj01NJtC=a#8$YK!s*bC=W!bxd6q1iFFx38+z1vNhp%*=d)udjao{OsqgRvpGP$TrDB|5Om~tP56GmCB*B zv;;jjH?rKU(?NQDjXVr@`PeKSo!bzk#w=&RzEi6~S1LdpkCdzlBdmDDYXeLiZ)0|L zK~GKs{r*^4hOG5)*fU7tc&j`$1$=xkQjb6OEQ3q7M-QJGHo!!YZyp~JF+i8gaHUkf zNtH_0le7GkYe^U)>-B6E;wdUHvPWZ(bi2sf?Xd-DhL$MnDFUMCQhA>5F*#9cY=FxO z$y(3w8JvVc;7_yfYVci*e7obonV3K~2&`wg3&$@(@MO3@kLgx~e`zo;jAiL50@%~5 z`17;Ty1z%C2jy`aE=?{wVkH5blQm(46;Bb6BnehmR+{hp!tQQvo>~tN=)Ju`vt=Ur yFLSacjIiP