Beremiz_service.py
branch1.1 Korean release
changeset 1280 72a826dfcfbb
parent 1270 aa9bc3e6181d
child 1434 6e0cd0ceabb7
equal deleted inserted replaced
977:c8e008b8cefe 1280:72a826dfcfbb
    21 #You should have received a copy of the GNU General Public
    21 #You should have received a copy of the GNU General Public
    22 #License along with this library; if not, write to the Free Software
    22 #License along with this library; if not, write to the Free Software
    23 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    23 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    24 
    24 
    25 import os, sys, getopt
    25 import os, sys, getopt
    26 from threading import Thread,Timer
    26 from threading import Thread
    27 
    27 
    28 def usage():
    28 def usage():
    29     print """
    29     print """
    30 Usage of Beremiz PLC execution service :\n
    30 Usage of Beremiz PLC execution service :\n
    31 %s {[-n servicename] [-i IP] [-p port] [-x enabletaskbar] [-a autostart]|-h|--help} working_dir
    31 %s {[-n servicename] [-i IP] [-p port] [-x enabletaskbar] [-a autostart]|-h|--help} working_dir
   112         
   112         
   113         # Import module for internationalization
   113         # Import module for internationalization
   114         import gettext
   114         import gettext
   115         
   115         
   116         CWD = os.path.split(os.path.realpath(__file__))[0]
   116         CWD = os.path.split(os.path.realpath(__file__))[0]
       
   117         def Bpath(*args):
       
   118             return os.path.join(CWD,*args)
   117         
   119         
   118         # Get folder containing translation files
   120         # Get folder containing translation files
   119         localedir = os.path.join(CWD,"locale")
   121         localedir = os.path.join(CWD,"locale")
   120         # Get the default language
   122         # Get the default language
   121         langid = wx.LANGUAGE_DEFAULT
   123         langid = wx.LANGUAGE_DEFAULT
   136             return wx.GetTranslation(message).encode("utf-8")
   138             return wx.GetTranslation(message).encode("utf-8")
   137 
   139 
   138         if __name__ == '__main__':
   140         if __name__ == '__main__':
   139             __builtin__.__dict__['_'] = wx.GetTranslation#unicode_translation
   141             __builtin__.__dict__['_'] = wx.GetTranslation#unicode_translation
   140         
   142         
   141         try:
   143         defaulticon = wx.Image(Bpath("images", "brz.png"))
   142             from wx.lib.embeddedimage import PyEmbeddedImage
   144         starticon = wx.Image(Bpath("images", "icoplay24.png"))
   143         except:
   145         stopicon = wx.Image(Bpath("images", "icostop24.png"))
   144             import cStringIO
       
   145             import base64
       
   146             
       
   147             class PyEmbeddedImage:
       
   148                 def __init__(self, image_string):
       
   149                     stream = cStringIO.StringIO(base64.b64decode(image_string))
       
   150                     self.Image = wx.ImageFromStream(stream)
       
   151                 def GetImage(self):
       
   152                     return self.Image
       
   153         
       
   154         defaulticon = PyEmbeddedImage(
       
   155         "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgIfAhkiAAABc5J"
       
   156         "REFUSIl9lW1MW9cZx3/n2vf6BQO2MZiXGBISILCVUEUlitYpjaKpXZJ1XZZ2kzJVY9r6IeLD"
       
   157         "pGTaNG3KtGmNNGlbpW3VFhRp0l6aZCllpVUqtVNJtBFKE5QXLxCjpCYEY7DBr9hcm3vPPgQY"
       
   158         "IQmPdKR7/vd5/v/n5dxzhZSSNeYBOoGDQGcoFPINDAyUDQ0NOUdGRmyGYSiBQGCpoaGhuGnT"
       
   159         "psShQ4f6WltbewEBVAK3gCBgrjJKKZFSKlLKeillt5Ty40gkMnnw4MFFQG60ysrKZHd3dyoe"
       
   160         "j//bNM0Le/fuPd/e3r5lmRMpJWK5ghrgFeBIT09P4/Hjx73pdFo47HaaNlfRutnJru0OKsoE"
       
   161         "E3GVqaSNa6EUw1dvIKWkoqKCrVu3FoeHh9WamppfRiKRn6wUYAUcwE7g2e7u7vrTp09XGIZB"
       
   162         "W1Mdv3qtmoBPrG0hHVsMhKLj6nqOqOWn/Pjnv2dgYIC5uTl1uSM71/pbgUbg6bNnz/rPnDnj"
       
   163         "dzoddO0P8Oo+jY2suDDD1Zv9DA1dfghXVbVBCFEqpcwAKEDTxMSE58SJE8+oqsq3nq/l1X0a"
       
   164         "QihYtNLHLqRET03wuYp7fO9r26mpKlsVUBSl0W63V6/shZTyyIEDB344Njb21JYaG7/5bgkA"
       
   165         "Dm8zTS/+7bHZLy0mSN+7yNztt8nPjYHFwfvXDf1P70zZ0ok0LS0tZy9fvvxNAGswGFQnJyef"
       
   166         "KnM5+NHLzuUDsrFZ7R68zS/hrGon1PcNMPI0BIzs9tcCNvNfDqxW64uqqvqKxWJc6e3trVVV"
       
   167         "leaAk6ryJ5N/9tH3GXv7Je7/5xermN3diMPXCkDfgrkg3UU0txWLxeLw+/1fB1BGR0frbTYb"
       
   168         "TXWWDbNeysUoZKbIRIZBPviOzKU8ejLMHyPFcMprrweQ7iUAXC7XPiGEak2lUk02m42mWn1D"
       
   169         "gfrnTiKNIrbyzSAUjEKWCx+/Mf+HyELBrLBvBhAIKDdgGsrLy+sAv1UIUa1pGv7yxQ0FbGX1"
       
   170         "D+0LQmHW7fVavE5Mo/gAFCCcoOs6NpvNA7gVRVGCmqYRz1hXg7NFU39rjshawjcuvs4P+o/y"
       
   171         "24uvE1+I4VCdfGfXUb76+VdWfQQCkbJSKBQoFApJTdMsCvApQDSlAjCTN7I/y5CNllpq1wqE"
       
   172         "YmPciIzwwdi7BKevreK7Gp5dfbYoFoozJrquo+v6rMViWbQCV4QQzGTsQJY3kzIhvFpgfYte"
       
   173         "7jhCMp9kk7uep+ueWcWj6f8Xqioq8ck0xcIS6XT6vpRy3gqMqKpqRBfKLLNF1ZRV6YBiPDrw"
       
   174         "vduefwTL6hl6b74FgFVR0T4rJTU3jcvlymcymal8Ph+z9vf3p7u6uv5y/vz5bw994ld2fmUH"
       
   175         "7nYFRVG4Gb3Guv8FpmmQzCcIJ+5w8c5HRFL3UYRC+ZKX633j6LpObW3tDcMwrsODq4Jbt27V"
       
   176         "HT58+N7o6KgCYHfY2f2lXfi+6CJbnsAwjUeyXzFFKLgdHqb+mmL8xh22bduWmJycfHN2dvbX"
       
   177         "uVwuoQC0tbXlKisrYytBi/lFZsKzOErtTyQWCOxWO36ljvl/FLk+dJOSkhJTUZR35+fn+3K5"
       
   178         "XAIeXNcASz6fbxzwrxDYVQdqpARvs498IYchDUxpogiBVVFxqE7U/5Zx4c8fEo/FKS0tlR0d"
       
   179         "HZ8ODg6+l06nr6zwrAp4PJ6Qpmlf2L9/fywYDFaOXB0RI1dHaGpuoq29Fa1Uxe62YeZMInei"
       
   180         "jAY/IRqNAtDZ2blUV1fXPzg4+F5VVdU/H6p0eYjqsWPHvnz37t0XwuHw7d27d4eTyeTvLl26"
       
   181         "FJiamnpim6qrq9mzZ094fHz875FI5J3p6ekr631WBARgaWlpCezYsePeuXPnzFAo5Dp58uS+"
       
   182         "dDp91GKxNBYKBW82m3Vomqa7XK7pbDYbnJmZuR2LxYL5fP79WCyWeeys1h/D9e97enqsp06d"
       
   183         "8mWzWU+xWPTkcjmXaZpxwzDCsVhsbqNggP8BMJOU3UUUf+0AAAAASUVORK5CYII=")
       
   184         
       
   185         #----------------------------------------------------------------------
       
   186         starticon = PyEmbeddedImage(
       
   187         "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgIfAhkiAAABbpJ"
       
   188         "REFUSIl9lltsFNcdxn9nZnbHs15fd23j9TXYC0UCKzEhMQ+oIS2g1kQ1pbFStX0opFWsovSh"
       
   189         "rUqp2pS2ioTUolaKFOGHqGkiJcKRuDhOaZRiZCsCXyBgCBBfMfbu+oa9s17wzuzl9MH24mDD"
       
   190         "XzoPc/6fft+c72jOGSGlZEVlAU8D9cB20zQ9HR0duRcvXszq7e01EomEUlFREa+srLR8Pl+g"
       
   191         "sbHx3zk5ORcAFfACA8Bt4CFUSomUUkgpS6SUB6SUH5umOXLgwIEHqqrKJfGao7S0VB49ejRo"
       
   192         "2/YnUsrT+/fvb66pqSldYiKlRCytoBB4Gfjx6dOnq5qamjwTExOKqqqU+QrYUJFN7QY32Qbc"
       
   193         "vSeYCGtcux1i5M5dAPx+P1VVVQvnzp0ziouLfx8MBt9cXoAGZABbgZ1HjhwpO378eEEymaSi"
       
   194         "tIBjPy9lU5nKoyWExF2yjy+mN3HsH+/Q3d3NwMCAsZTI9pVaDXgK2Hr27Nn85ubmEpdh8IMX"
       
   195         "ffxirwshVrGXHBQSC/dIRvoZGuz/WkvTtHIhhCGlXABQgI2Tk5P5hw8f3uZwOGj8VjGHXnoC"
       
   196         "HJCpJFbkLtr8FXbX+XC79HRPVVW/qqre9LtIKX/S0NDwy76+vq1lhTr/fM2NAmTk+fHv/dea"
       
   197         "BlZkDHP0PHODH2NHg1gykw8/X7Dfb7vjTNgJqqurT3R1db0GoF2/fl0fGhqqdWca/K7RhZLO"
       
   198         "WSBU55oGGXlVZORVkeV7nsFPDqKL+9TWJCI3n9rojX2mYhjGj4QQv5FSziunTp0qdjqd4hvl"
       
   199         "Lnz5j49lrPMNhv7zM6b63knPuQpryMj3A9A2L++nvDaZXheqqrrXrVu3D0C5detWudPpxO/T"
       
   200         "Hk8HYnOD3J+8yr3bH6XnZNImHg3xfsgenfHo5QAyJwFAdnb2HiGEppmmWa3rOhtKrCcalNT9"
       
   201         "llTSwvBsXISn4nRdbJ5/czRsWvlGhQAEYtFg0kl2dnYZUKgB5U6nk5L82BMNXIU1X3uOWFH5"
       
   202         "eWIuy/YYWcjU4qQAxQ22bWMYhgfIU1RV/UrXdWaiDyOyUiLROktoJfDtC8fZfWQbb//v75ix"
       
   203         "MDlGnvjVC3+gflNDWiMQKPMalmVh2/a8w+HQFKAHIBR2ABCOS+uN6cTMoFstXmlwZbSba7tv"
       
   204         "8hfzT7z+7k+ZnZ0BoK5yR1qjCBV7MoVt29i2PaWqqq0BvUIIQqYORHlrKj6R9BoVj0b04oY9"
       
   205         "nEt+yvz3Y5yR/+Xap3XsDb/EtvV1aY1DdTA7HsW2bCKRyLiUclYBelRVldNWAfPSm4oV5ZQJ"
       
   206         "Vn/G9Zv2oWt6Ous7e4K81XiC1wNNBO6OIWKgB7Mwp000TYuFw+GxWCw2qbS2tk7k5uae/eDD"
       
   207         "Fn594p6SFyxRCjKLUBWF8fBoegTNMVLLm/kwdMyGGON/nePLklv0dl/Cii3gdrtvAzdg8aig"
       
   208         "vb296uDBgwMjIyMCwFvoZXv9NvRnIKqHSckUyQdJrtfexPqm5LGVAuNdVaofcCVywfpexLYD"
       
   209         "CsDOnTvnioqKzGXdzNQMV9tvkJEyUITyeOAjpYyAc9gxYc/GWyK2HYDF4xog6fV6h1i8FwCo"
       
   210         "LK/EncwhkWGxEH9AXLMXM2H1CpQBifI3yeapZ+70d43+cSo4+95yL23g8XiGFUWp3bVrV/Ty"
       
   211         "5ctZnR2ddHZ08uxzz1K9eT1GRhJls1gFlsfieK+WpJ5e/3z7pcuXzmia1rJSs3xlOg8dOvTD"
       
   212         "8fHx7wQCgb4tW7bMm6b55/Pnz+eGw+FFGJDT5iT1XRWlfxHMZ06+/Vz9dCAQeG9kZKR1x44d"
       
   213         "nSdPnkyuZSAArbq6eqOiKAP9/f3xlpaWgra2tlei0eiryWSyKGKa2TcaL+muwcxU5aDf9Gi+"
       
   214         "L0Oh0BehUOiaZVlnAoHAzFr7Ih75bVnVb2pqcvf09Phi0ei6+/rUC6lw1k0p5bSUctThcIwP"
       
   215         "Dw/HnwT4P6CDl+TMvD0JAAAAAElFTkSuQmCC")
       
   216         
       
   217         #----------------------------------------------------------------------
       
   218         stopicon = PyEmbeddedImage(
       
   219         "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgIfAhkiAAABPRJ"
       
   220         "REFUSImdlllsVGUUx3/f/e4sd5iZLjNt6XSFdtgkjWFRePABDaCBGgjamIg81CU0aoxbRHww"
       
   221         "+EDkhWjEB5rYGEMUxQTCJg8EoQ2BbgrFCNJWltplgC63naEzd+bO50NLLVAq4STfwz3nfP/f"
       
   222         "PSf3O98VSikmmQ94HFgDLDdNM1BfX5955swZX0tLi5FKpbSSkpJkaWlpIhQKdVdVVX2XkZFx"
       
   223         "EpBAEGgHLgH/iSqlUEoJpVSBUqpaKXXYNM0r1dXVt6WUajx5ylVYWKi2bdvWY1nWUaXUgQ0b"
       
   224         "NtRWVFQUjmuilEKMV5ALvAhsPHDgQFlNTU2gr69Pk1JSFMphTomfRXO8+A243i/oG9I5f6mX"
       
   225         "K1evAxAOhykrKxs9duyYkZ+f/0lPT8/2OwXogBtYDKzYunVr0c6dO3Ns26akMIcdbxQyv0hy"
       
   226         "rwmh8Bas5/eb89nxRR1NTU20t7cb4x1ZPjlXB2YBiw8ePJhdW1tb4DEMXng6xJtrPQhxn/Y4"
       
   227         "QSM12o89fJnOjst3hXRdLxZCGEqpUQANmBuJRLK3bNmy1OFwUPVMPm9VTiMOqLRNYvg6+shv"
       
   228         "rFoWwutxTcSklGEpZXDiXZRSr6xbt+6dtra2xUW5Lr7c7EUD3Flhwmu/nRKQGO7CvHaCwY7D"
       
   229         "WNEeEmoGe0+PWnuOXHWmrBTl5eW7GxsbNwPoFy5ccHV2di7yzjD4uMqDNtFngZDOKQHurDLc"
       
   230         "WWX4Qk/ScfRVXCLGoorU8J+z5gbjxyWGYbwshPhQKTWi7d+/P9/pdIp5xR5C2Q9uS1fDp3T+"
       
   231         "8jo32uomfJ7cCtzZYQCOjKhYOmgxI+hBSumdOXPmegDt4sWLxU6nk3BIf7A6EB/sIBY5R/+l"
       
   232         "nyd8yrZIRnvZ02tduxVwFQOojBQAfr9/tRBC103TLHe5XMwpSEwLKFj2EWk7gRGYOyaeTtJ4"
       
   233         "pnZk+7UhM5FtlAhAIMYAESd+v78IyNWBYqfTSUF2fFqAJ7firufhRFSdTg36rIDhQ6XHnAI0"
       
   234         "L1iWhWEYASBLl1L+JaWcfSuqk+u3AUikRer4ADffg/w7gt80fs35r34k3BYh2xNAarooAJ4d"
       
   235         "vsHgaP8EWMR17GiaVo8r0+Fw6DrQDDzXO+RgQSjBUFIlPh+wB0vLZD6TrLWrkWRXB29fGAK6"
       
   236         "pql1rNXVmrCklJYGtAgh6DXHDsuuG8k+O9M5895tq+atpSwwZ9o2TjZlWTGl1IAGNEsp1c1E"
       
   237         "DiMqmI7nZRQJ7j/G6xZWMS/vsYcGkEzG4vF4RDt06FBfZmbmwR/27uOD3f1aVk+BljMjD6lp"
       
   238         "/DN07a4VTYw8tL4rrQZgbNixadOm90+dOvX82cZmcbaxmWBukOVrlvJudw1R1xDp8a+kuPM6"
       
   239         "Gx8S4LXtCIwNO1asWDGYl5dn3gneunGLc7/+gTttoAntQRrTmgMmpimAHQwGOycnlBaX4rUz"
       
   240         "8LszMRweXLr7kWB35oMdCAT+1jRt0cqVK6Otra2+hvoGGuobWPLEEsoXzkbPkLhvR4CBRwJY"
       
   241         "Xq/3SGVlZbq7u7utsrJyxDTNz06cOJHZ0tRCS1MLAKuRwNQT9v8AyV27dn1fXl7eqmlae11d"
       
   242         "XXLfvn0/+Xy+l6LR6Gu2befFYjFfzrk2FzeHp7mK7jdxz2/LffGamhpvc3NzyLKsbFd3z1PG"
       
   243         "aHyBTKdjum0POGzbFAp7qo0xVOtJZdf/C/wRDnL5FYGSAAAAAElFTkSuQmCC")
       
   244         
   146         
   245         class ParamsEntryDialog(wx.TextEntryDialog):
   147         class ParamsEntryDialog(wx.TextEntryDialog):
   246             if wx.VERSION < (2, 6, 0):
   148             if wx.VERSION < (2, 6, 0):
   247                 def Bind(self, event, function, id = None):
   149                 def Bind(self, event, function, id = None):
   248                     if id is not None:
   150                     if id is not None:
   347                 return icon
   249                 return icon
   348             
   250             
   349             def OnTaskBarStartPLC(self, evt):
   251             def OnTaskBarStartPLC(self, evt):
   350                 if self.pyroserver.plcobj is not None: 
   252                 if self.pyroserver.plcobj is not None: 
   351                     self.pyroserver.plcobj.StartPLC()
   253                     self.pyroserver.plcobj.StartPLC()
   352                 evt.Skip()
       
   353             
   254             
   354             def OnTaskBarStopPLC(self, evt):
   255             def OnTaskBarStopPLC(self, evt):
   355                 if self.pyroserver.plcobj is not None:
   256                 if self.pyroserver.plcobj is not None:
   356                     Thread(target=self.pyroserver.plcobj.StopPLC).start()
   257                     Thread(target=self.pyroserver.plcobj.StopPLC).start()
   357                 evt.Skip()
       
   358             
   258             
   359             def OnTaskBarChangeInterface(self, evt):
   259             def OnTaskBarChangeInterface(self, evt):
   360                 dlg = ParamsEntryDialog(None, _("Enter the IP of the interface to bind"), defaultValue=self.pyroserver.ip_addr)
   260                 dlg = ParamsEntryDialog(None, _("Enter the IP of the interface to bind"), defaultValue=self.pyroserver.ip_addr)
   361                 dlg.SetTests([(re.compile('\d{1,3}(?:\.\d{1,3}){3}$').match, _("IP is not valid!")),
   261                 dlg.SetTests([(re.compile('\d{1,3}(?:\.\d{1,3}){3}$').match, _("IP is not valid!")),
   362                                ( lambda x :len([x for x in x.split(".") if 0 <= int(x) <= 255]) == 4, _("IP is not valid!"))
   262                                ( lambda x :len([x for x in x.split(".") if 0 <= int(x) <= 255]) == 4, _("IP is not valid!"))
   363                                ])
   263                                ])
   364                 if dlg.ShowModal() == wx.ID_OK:
   264                 if dlg.ShowModal() == wx.ID_OK:
   365                     self.pyroserver.ip_addr = dlg.GetValue()
   265                     self.pyroserver.ip_addr = dlg.GetValue()
   366                     self.pyroserver.Stop()
   266                     self.pyroserver.Stop()
   367                 evt.Skip()
       
   368             
   267             
   369             def OnTaskBarChangePort(self, evt):
   268             def OnTaskBarChangePort(self, evt):
   370                 dlg = ParamsEntryDialog(None, _("Enter a port number "), defaultValue=str(self.pyroserver.port))
   269                 dlg = ParamsEntryDialog(None, _("Enter a port number "), defaultValue=str(self.pyroserver.port))
   371                 dlg.SetTests([(UnicodeType.isdigit, _("Port number must be an integer!")), (lambda port : 0 <= int(port) <= 65535 , _("Port number must be 0 <= port <= 65535!"))])
   270                 dlg.SetTests([(UnicodeType.isdigit, _("Port number must be an integer!")), (lambda port : 0 <= int(port) <= 65535 , _("Port number must be 0 <= port <= 65535!"))])
   372                 if dlg.ShowModal() == wx.ID_OK:
   271                 if dlg.ShowModal() == wx.ID_OK:
   373                     self.pyroserver.port = int(dlg.GetValue())
   272                     self.pyroserver.port = int(dlg.GetValue())
   374                     self.pyroserver.Stop()
   273                     self.pyroserver.Stop()
   375                 evt.Skip()
       
   376             
   274             
   377             def OnTaskBarChangeWorkingDir(self, evt):
   275             def OnTaskBarChangeWorkingDir(self, evt):
   378                 dlg = wx.DirDialog(None, _("Choose a working directory "), self.pyroserver.workdir, wx.DD_NEW_DIR_BUTTON)
   276                 dlg = wx.DirDialog(None, _("Choose a working directory "), self.pyroserver.workdir, wx.DD_NEW_DIR_BUTTON)
   379                 if dlg.ShowModal() == wx.ID_OK:
   277                 if dlg.ShowModal() == wx.ID_OK:
   380                     self.pyroserver.workdir = dlg.GetPath()
   278                     self.pyroserver.workdir = dlg.GetPath()
   381                     self.pyroserver.Stop()
   279                     self.pyroserver.Stop()
   382                 evt.Skip()
       
   383             
   280             
   384             def OnTaskBarChangeName(self, evt):
   281             def OnTaskBarChangeName(self, evt):
   385                 dlg = ParamsEntryDialog(None, _("Enter a name "), defaultValue=self.pyroserver.name)
   282                 dlg = ParamsEntryDialog(None, _("Enter a name "), defaultValue=self.pyroserver.name)
   386                 dlg.SetTests([(lambda name : len(name) is not 0 , _("Name must not be null!"))])
   283                 dlg.SetTests([(lambda name : len(name) is not 0 , _("Name must not be null!"))])
   387                 if dlg.ShowModal() == wx.ID_OK:
   284                 if dlg.ShowModal() == wx.ID_OK:
   388                     self.pyroserver.name = dlg.GetValue()
   285                     self.pyroserver.name = dlg.GetValue()
   389                     self.pyroserver.Restart()
   286                     self.pyroserver.Restart()
   390                 evt.Skip()
       
   391             
   287             
   392             def _LiveShellLocals(self):
   288             def _LiveShellLocals(self):
   393                 if self.pyroserver.plcobj is not None:
   289                 if self.pyroserver.plcobj is not None:
   394                     return {"locals":self.pyroserver.plcobj.python_threads_vars}
   290                     return {"locals":self.pyroserver.plcobj.python_runtime_vars}
   395                 else:
   291                 else:
   396                     return {}
   292                     return {}
   397 
   293             
   398             def OnTaskBarLiveShell(self, evt):
   294             def OnTaskBarLiveShell(self, evt):
   399                 from wx import py
   295                 from wx import py
   400                 frame = py.crust.CrustFrame(**self._LiveShellLocals())
   296                 frame = py.crust.CrustFrame(**self._LiveShellLocals())
   401                 frame.Show()
   297                 frame.Show()
   402                 evt.Skip()
       
   403             
   298             
   404             def OnTaskBarWXInspector(self, evt):
   299             def OnTaskBarWXInspector(self, evt):
   405                 # Activate the widget inspection tool
   300                 # Activate the widget inspection tool
   406                 from wx.lib.inspection import InspectionTool
   301                 from wx.lib.inspection import InspectionTool
   407                 if not InspectionTool().initialized:
   302                 if not InspectionTool().initialized:
   408                     InspectionTool().Init(**self._LiveShellLocals())
   303                     InspectionTool().Init(**self._LiveShellLocals())
   409 
   304 
   410                 wnd = wx.GetApp()
   305                 wnd = wx.GetApp()
   411                 InspectionTool().Show(wnd, True)
   306                 InspectionTool().Show(wnd, True)
   412 
       
   413                 evt.Skip()
       
   414             
   307             
   415             def OnTaskBarQuit(self, evt):
   308             def OnTaskBarQuit(self, evt):
   416                 Thread(target=self.pyroserver.Quit).start()
   309                 if wx.Platform == '__WXMSW__':
       
   310                     Thread(target=self.pyroserver.Quit).start()
   417                 self.RemoveIcon()
   311                 self.RemoveIcon()
   418                 wx.CallAfter(wx.GetApp().Exit)
   312                 wx.CallAfter(wx.GetApp().ExitMainLoop)
   419                 evt.Skip()
       
   420             
   313             
   421             def UpdateIcon(self, plcstatus):
   314             def UpdateIcon(self, plcstatus):
   422                 if plcstatus is "Started" :
   315                 if plcstatus is "Started" :
   423                     currenticon = self.MakeIcon(starticon.GetImage())
   316                     currenticon = self.MakeIcon(starticon)
   424                 elif plcstatus is "Stopped":
   317                 elif plcstatus is "Stopped":
   425                     currenticon = self.MakeIcon(stopicon.GetImage())
   318                     currenticon = self.MakeIcon(stopicon)
   426                 else:
   319                 else:
   427                     currenticon = self.MakeIcon(defaulticon.GetImage())
   320                     currenticon = self.MakeIcon(defaulticon)
   428                 self.SetIcon(currenticon, "Beremiz Service")
   321                 self.SetIcon(currenticon, "Beremiz Service")
   429 
   322 
   430 from runtime import PLCObject, PLCprint, ServicePublisher
   323 from runtime import PLCObject, PLCprint, ServicePublisher
   431 import Pyro.core as pyro
   324 import Pyro.core as pyro
   432 
   325 
   434     os.mkdir(WorkingDir)
   327     os.mkdir(WorkingDir)
   435 
   328 
   436 def default_evaluator(tocall, *args, **kwargs):
   329 def default_evaluator(tocall, *args, **kwargs):
   437     try:
   330     try:
   438         res=(tocall(*args,**kwargs), None)
   331         res=(tocall(*args,**kwargs), None)
   439     except Exception,exp:
   332     except Exception:
   440         res=(None, exp)
   333         res=(None, sys.exc_info())
   441     return res
   334     return res
   442 
   335 
   443 class Server():
   336 class Server():
   444     def __init__(self, servicename, ip_addr, port, workdir, argv, autostart=False, statuschange=None, evaluator=default_evaluator, website=None):
   337     def __init__(self, servicename, ip_addr, port, workdir, argv, autostart=False, statuschange=None, evaluator=default_evaluator, website=None):
   445         self.continueloop = True
   338         self.continueloop = True
   463     def Restart(self):
   356     def Restart(self):
   464         self.Stop()
   357         self.Stop()
   465 
   358 
   466     def Quit(self):
   359     def Quit(self):
   467         self.continueloop = False
   360         self.continueloop = False
       
   361         if self.plcobj is not None:
       
   362             self.plcobj.UnLoadPLC()
   468         self.Stop()
   363         self.Stop()
   469 
   364 
   470     def Start(self):
   365     def Start(self):
   471         pyro.initServer()
   366         pyro.initServer()
   472         self.daemon=pyro.Daemon(host=self.ip_addr, port=self.port)
   367         self.daemon=pyro.Daemon(host=self.ip_addr, port=self.port)
   485             self.ip_addr != "127.0.0.1"):
   380             self.ip_addr != "127.0.0.1"):
   486             print "Publishing service on local network"
   381             print "Publishing service on local network"
   487             self.servicepublisher = ServicePublisher.ServicePublisher()
   382             self.servicepublisher = ServicePublisher.ServicePublisher()
   488             self.servicepublisher.RegisterService(self.servicename, self.ip_addr, self.port)
   383             self.servicepublisher.RegisterService(self.servicename, self.ip_addr, self.port)
   489         
   384         
   490         if self.autostart:
   385         if self.autostart and self.plcobj.GetPLCstatus()[0] != "Empty":
   491             self.plcobj.StartPLC()
   386             self.plcobj.StartPLC()
   492         
   387         
   493         sys.stdout.flush()
   388         sys.stdout.flush()
   494         
   389         
   495         self.daemon.requestLoop()
   390         self.daemon.requestLoop()
   496     
   391     
   497     def Stop(self):
   392     def Stop(self):
   498         self.plcobj.StopPLC()
   393         if self.plcobj is not None:
       
   394             self.plcobj.StopPLC()
   499         if self.servicepublisher is not None:
   395         if self.servicepublisher is not None:
   500             self.servicepublisher.UnRegisterService()
   396             self.servicepublisher.UnRegisterService()
   501             self.servicepublisher = None
   397             self.servicepublisher = None
   502         self.daemon.shutdown(True)
   398         self.daemon.shutdown(True)
   503 
   399 
   708     pyroserver = Server(servicename, given_ip, port, WorkingDir, argv, autostart, statuschange, evaluator, website)
   604     pyroserver = Server(servicename, given_ip, port, WorkingDir, argv, autostart, statuschange, evaluator, website)
   709     taskbar_instance = BeremizTaskBarIcon(pyroserver, enablewx)
   605     taskbar_instance = BeremizTaskBarIcon(pyroserver, enablewx)
   710 else:
   606 else:
   711     pyroserver = Server(servicename, given_ip, port, WorkingDir, argv, autostart, website=website)
   607     pyroserver = Server(servicename, given_ip, port, WorkingDir, argv, autostart, website=website)
   712 
   608 
       
   609 # Exception hooks s
       
   610 import threading, traceback
       
   611 def LogException(*exp):
       
   612     if pyroserver.plcobj is not None:
       
   613         pyroserver.plcobj.LogMessage(0,'\n'.join(traceback.format_exception(*exp)))
       
   614     else:
       
   615         traceback.print_exception(*exp)
       
   616 
       
   617 sys.excepthook = LogException
       
   618 def installThreadExcepthook():
       
   619     init_old = threading.Thread.__init__
       
   620     def init(self, *args, **kwargs):
       
   621         init_old(self, *args, **kwargs)
       
   622         run_old = self.run
       
   623         def run_with_except_hook(*args, **kw):
       
   624             try:
       
   625                 run_old(*args, **kw)
       
   626             except (KeyboardInterrupt, SystemExit):
       
   627                 raise
       
   628             except:
       
   629                 sys.excepthook(*sys.exc_info())
       
   630         self.run = run_with_except_hook
       
   631     threading.Thread.__init__ = init
       
   632 installThreadExcepthook()
       
   633 
   713 if havetwisted or havewx:
   634 if havetwisted or havewx:
   714     pyro_thread=Thread(target=pyroserver.Loop)
   635     pyro_thread=Thread(target=pyroserver.Loop)
   715     pyro_thread.start()
   636     pyro_thread.start()
   716 
   637 
   717     if havetwisted:
   638     if havetwisted: