Beremiz_service.py
changeset 271 ea7928fd07da
parent 269 d29c5f71574f
child 290 3bd617ae7a05
equal deleted inserted replaced
270:3b8fb275cf7e 271:ea7928fd07da
    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 
    26 
    27 try:
       
    28     import wx, re
       
    29     from wx.lib.embeddedimage import PyEmbeddedImage
       
    30     from threading import Thread
       
    31     from types import *
       
    32     havewx = True
       
    33 except:
       
    34     havewx = False
       
    35 
       
    36 if havewx:
       
    37     defaulticon = PyEmbeddedImage(
       
    38     "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgIfAhkiAAABc5J"
       
    39     "REFUSIl9lW1MW9cZx3/n2vf6BQO2MZiXGBISILCVUEUlitYpjaKpXZJ1XZZ2kzJVY9r6IeLD"
       
    40     "pGTaNG3KtGmNNGlbpW3VFhRp0l6aZCllpVUqtVNJtBFKE5QXLxCjpCYEY7DBr9hcm3vPPgQY"
       
    41     "IQmPdKR7/vd5/v/n5dxzhZSSNeYBOoGDQGcoFPINDAyUDQ0NOUdGRmyGYSiBQGCpoaGhuGnT"
       
    42     "psShQ4f6WltbewEBVAK3gCBgrjJKKZFSKlLKeillt5Ty40gkMnnw4MFFQG60ysrKZHd3dyoe"
       
    43     "j//bNM0Le/fuPd/e3r5lmRMpJWK5ghrgFeBIT09P4/Hjx73pdFo47HaaNlfRutnJru0OKsoE"
       
    44     "E3GVqaSNa6EUw1dvIKWkoqKCrVu3FoeHh9WamppfRiKRn6wUYAUcwE7g2e7u7vrTp09XGIZB"
       
    45     "W1Mdv3qtmoBPrG0hHVsMhKLj6nqOqOWn/Pjnv2dgYIC5uTl1uSM71/pbgUbg6bNnz/rPnDnj"
       
    46     "dzoddO0P8Oo+jY2suDDD1Zv9DA1dfghXVbVBCFEqpcwAKEDTxMSE58SJE8+oqsq3nq/l1X0a"
       
    47     "QihYtNLHLqRET03wuYp7fO9r26mpKlsVUBSl0W63V6/shZTyyIEDB344Njb21JYaG7/5bgkA"
       
    48     "Dm8zTS/+7bHZLy0mSN+7yNztt8nPjYHFwfvXDf1P70zZ0ok0LS0tZy9fvvxNAGswGFQnJyef"
       
    49     "KnM5+NHLzuUDsrFZ7R68zS/hrGon1PcNMPI0BIzs9tcCNvNfDqxW64uqqvqKxWJc6e3trVVV"
       
    50     "leaAk6ryJ5N/9tH3GXv7Je7/5xermN3diMPXCkDfgrkg3UU0txWLxeLw+/1fB1BGR0frbTYb"
       
    51     "TXWWDbNeysUoZKbIRIZBPviOzKU8ejLMHyPFcMprrweQ7iUAXC7XPiGEak2lUk02m42mWn1D"
       
    52     "gfrnTiKNIrbyzSAUjEKWCx+/Mf+HyELBrLBvBhAIKDdgGsrLy+sAv1UIUa1pGv7yxQ0FbGX1"
       
    53     "D+0LQmHW7fVavE5Mo/gAFCCcoOs6NpvNA7gVRVGCmqYRz1hXg7NFU39rjshawjcuvs4P+o/y"
       
    54     "24uvE1+I4VCdfGfXUb76+VdWfQQCkbJSKBQoFApJTdMsCvApQDSlAjCTN7I/y5CNllpq1wqE"
       
    55     "YmPciIzwwdi7BKevreK7Gp5dfbYoFoozJrquo+v6rMViWbQCV4QQzGTsQJY3kzIhvFpgfYte"
       
    56     "7jhCMp9kk7uep+ueWcWj6f8Xqioq8ck0xcIS6XT6vpRy3gqMqKpqRBfKLLNF1ZRV6YBiPDrw"
       
    57     "vduefwTL6hl6b74FgFVR0T4rJTU3jcvlymcymal8Ph+z9vf3p7u6uv5y/vz5bw994ld2fmUH"
       
    58     "7nYFRVG4Gb3Guv8FpmmQzCcIJ+5w8c5HRFL3UYRC+ZKX633j6LpObW3tDcMwrsODq4Jbt27V"
       
    59     "HT58+N7o6KgCYHfY2f2lXfi+6CJbnsAwjUeyXzFFKLgdHqb+mmL8xh22bduWmJycfHN2dvbX"
       
    60     "uVwuoQC0tbXlKisrYytBi/lFZsKzOErtTyQWCOxWO36ljvl/FLk+dJOSkhJTUZR35+fn+3K5"
       
    61     "XAIeXNcASz6fbxzwrxDYVQdqpARvs498IYchDUxpogiBVVFxqE7U/5Zx4c8fEo/FKS0tlR0d"
       
    62     "HZ8ODg6+l06nr6zwrAp4PJ6Qpmlf2L9/fywYDFaOXB0RI1dHaGpuoq29Fa1Uxe62YeZMInei"
       
    63     "jAY/IRqNAtDZ2blUV1fXPzg4+F5VVdU/H6p0eYjqsWPHvnz37t0XwuHw7d27d4eTyeTvLl26"
       
    64     "FJiamnpim6qrq9mzZ094fHz875FI5J3p6ekr631WBARgaWlpCezYsePeuXPnzFAo5Dp58uS+"
       
    65     "dDp91GKxNBYKBW82m3Vomqa7XK7pbDYbnJmZuR2LxYL5fP79WCyWeeys1h/D9e97enqsp06d"
       
    66     "8mWzWU+xWPTkcjmXaZpxwzDCsVhsbqNggP8BMJOU3UUUf+0AAAAASUVORK5CYII=")
       
    67     
       
    68     #----------------------------------------------------------------------
       
    69     starticon = PyEmbeddedImage(
       
    70     "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgIfAhkiAAABbpJ"
       
    71     "REFUSIl9lltsFNcdxn9nZnbHs15fd23j9TXYC0UCKzEhMQ+oIS2g1kQ1pbFStX0opFWsovSh"
       
    72     "rUqp2pS2ioTUolaKFOGHqGkiJcKRuDhOaZRiZCsCXyBgCBBfMfbu+oa9s17wzuzl9MH24mDD"
       
    73     "XzoPc/6fft+c72jOGSGlZEVlAU8D9cB20zQ9HR0duRcvXszq7e01EomEUlFREa+srLR8Pl+g"
       
    74     "sbHx3zk5ORcAFfACA8Bt4CFUSomUUkgpS6SUB6SUH5umOXLgwIEHqqrKJfGao7S0VB49ejRo"
       
    75     "2/YnUsrT+/fvb66pqSldYiKlRCytoBB4Gfjx6dOnq5qamjwTExOKqqqU+QrYUJFN7QY32Qbc"
       
    76     "vSeYCGtcux1i5M5dAPx+P1VVVQvnzp0ziouLfx8MBt9cXoAGZABbgZ1HjhwpO378eEEymaSi"
       
    77     "tIBjPy9lU5nKoyWExF2yjy+mN3HsH+/Q3d3NwMCAsZTI9pVaDXgK2Hr27Nn85ubmEpdh8IMX"
       
    78     "ffxirwshVrGXHBQSC/dIRvoZGuz/WkvTtHIhhCGlXABQgI2Tk5P5hw8f3uZwOGj8VjGHXnoC"
       
    79     "HJCpJFbkLtr8FXbX+XC79HRPVVW/qqre9LtIKX/S0NDwy76+vq1lhTr/fM2NAmTk+fHv/dea"
       
    80     "BlZkDHP0PHODH2NHg1gykw8/X7Dfb7vjTNgJqqurT3R1db0GoF2/fl0fGhqqdWca/K7RhZLO"
       
    81     "WSBU55oGGXlVZORVkeV7nsFPDqKL+9TWJCI3n9rojX2mYhjGj4QQv5FSziunTp0qdjqd4hvl"
       
    82     "Lnz5j49lrPMNhv7zM6b63knPuQpryMj3A9A2L++nvDaZXheqqrrXrVu3D0C5detWudPpxO/T"
       
    83     "Hk8HYnOD3J+8yr3bH6XnZNImHg3xfsgenfHo5QAyJwFAdnb2HiGEppmmWa3rOhtKrCcalNT9"
       
    84     "llTSwvBsXISn4nRdbJ5/czRsWvlGhQAEYtFg0kl2dnYZUKgB5U6nk5L82BMNXIU1X3uOWFH5"
       
    85     "eWIuy/YYWcjU4qQAxQ22bWMYhgfIU1RV/UrXdWaiDyOyUiLROktoJfDtC8fZfWQbb//v75ix"
       
    86     "MDlGnvjVC3+gflNDWiMQKPMalmVh2/a8w+HQFKAHIBR2ABCOS+uN6cTMoFstXmlwZbSba7tv"
       
    87     "8hfzT7z+7k+ZnZ0BoK5yR1qjCBV7MoVt29i2PaWqqq0BvUIIQqYORHlrKj6R9BoVj0b04oY9"
       
    88     "nEt+yvz3Y5yR/+Xap3XsDb/EtvV1aY1DdTA7HsW2bCKRyLiUclYBelRVldNWAfPSm4oV5ZQJ"
       
    89     "Vn/G9Zv2oWt6Ous7e4K81XiC1wNNBO6OIWKgB7Mwp000TYuFw+GxWCw2qbS2tk7k5uae/eDD"
       
    90     "Fn594p6SFyxRCjKLUBWF8fBoegTNMVLLm/kwdMyGGON/nePLklv0dl/Cii3gdrtvAzdg8aig"
       
    91     "vb296uDBgwMjIyMCwFvoZXv9NvRnIKqHSckUyQdJrtfexPqm5LGVAuNdVaofcCVywfpexLYD"
       
    92     "CsDOnTvnioqKzGXdzNQMV9tvkJEyUITyeOAjpYyAc9gxYc/GWyK2HYDF4xog6fV6h1i8FwCo"
       
    93     "LK/EncwhkWGxEH9AXLMXM2H1CpQBifI3yeapZ+70d43+cSo4+95yL23g8XiGFUWp3bVrV/Ty"
       
    94     "5ctZnR2ddHZ08uxzz1K9eT1GRhJls1gFlsfieK+WpJ5e/3z7pcuXzmia1rJSs3xlOg8dOvTD"
       
    95     "8fHx7wQCgb4tW7bMm6b55/Pnz+eGw+FFGJDT5iT1XRWlfxHMZ06+/Vz9dCAQeG9kZKR1x44d"
       
    96     "nSdPnkyuZSAArbq6eqOiKAP9/f3xlpaWgra2tlei0eiryWSyKGKa2TcaL+muwcxU5aDf9Gi+"
       
    97     "L0Oh0BehUOiaZVlnAoHAzFr7Ih75bVnVb2pqcvf09Phi0ei6+/rUC6lw1k0p5bSUctThcIwP"
       
    98     "Dw/HnwT4P6CDl+TMvD0JAAAAAElFTkSuQmCC")
       
    99     
       
   100     #----------------------------------------------------------------------
       
   101     stopicon = PyEmbeddedImage(
       
   102     "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgIfAhkiAAABPRJ"
       
   103     "REFUSImdlllsVGUUx3/f/e4sd5iZLjNt6XSFdtgkjWFRePABDaCBGgjamIg81CU0aoxbRHww"
       
   104     "+EDkhWjEB5rYGEMUxQTCJg8EoQ2BbgrFCNJWltplgC63naEzd+bO50NLLVAq4STfwz3nfP/f"
       
   105     "PSf3O98VSikmmQ94HFgDLDdNM1BfX5955swZX0tLi5FKpbSSkpJkaWlpIhQKdVdVVX2XkZFx"
       
   106     "EpBAEGgHLgH/iSqlUEoJpVSBUqpaKXXYNM0r1dXVt6WUajx5ylVYWKi2bdvWY1nWUaXUgQ0b"
       
   107     "NtRWVFQUjmuilEKMV5ALvAhsPHDgQFlNTU2gr69Pk1JSFMphTomfRXO8+A243i/oG9I5f6mX"
       
   108     "K1evAxAOhykrKxs9duyYkZ+f/0lPT8/2OwXogBtYDKzYunVr0c6dO3Ns26akMIcdbxQyv0hy"
       
   109     "rwmh8Bas5/eb89nxRR1NTU20t7cb4x1ZPjlXB2YBiw8ePJhdW1tb4DEMXng6xJtrPQhxn/Y4"
       
   110     "QSM12o89fJnOjst3hXRdLxZCGEqpUQANmBuJRLK3bNmy1OFwUPVMPm9VTiMOqLRNYvg6+shv"
       
   111     "rFoWwutxTcSklGEpZXDiXZRSr6xbt+6dtra2xUW5Lr7c7EUD3Flhwmu/nRKQGO7CvHaCwY7D"
       
   112     "WNEeEmoGe0+PWnuOXHWmrBTl5eW7GxsbNwPoFy5ccHV2di7yzjD4uMqDNtFngZDOKQHurDLc"
       
   113     "WWX4Qk/ScfRVXCLGoorU8J+z5gbjxyWGYbwshPhQKTWi7d+/P9/pdIp5xR5C2Q9uS1fDp3T+"
       
   114     "8jo32uomfJ7cCtzZYQCOjKhYOmgxI+hBSumdOXPmegDt4sWLxU6nk3BIf7A6EB/sIBY5R/+l"
       
   115     "nyd8yrZIRnvZ02tduxVwFQOojBQAfr9/tRBC103TLHe5XMwpSEwLKFj2EWk7gRGYOyaeTtJ4"
       
   116     "pnZk+7UhM5FtlAhAIMYAESd+v78IyNWBYqfTSUF2fFqAJ7firufhRFSdTg36rIDhQ6XHnAI0"
       
   117     "L1iWhWEYASBLl1L+JaWcfSuqk+u3AUikRer4ADffg/w7gt80fs35r34k3BYh2xNAarooAJ4d"
       
   118     "vsHgaP8EWMR17GiaVo8r0+Fw6DrQDDzXO+RgQSjBUFIlPh+wB0vLZD6TrLWrkWRXB29fGAK6"
       
   119     "pql1rNXVmrCklJYGtAgh6DXHDsuuG8k+O9M5895tq+atpSwwZ9o2TjZlWTGl1IAGNEsp1c1E"
       
   120     "DiMqmI7nZRQJ7j/G6xZWMS/vsYcGkEzG4vF4RDt06FBfZmbmwR/27uOD3f1aVk+BljMjD6lp"
       
   121     "/DN07a4VTYw8tL4rrQZgbNixadOm90+dOvX82cZmcbaxmWBukOVrlvJudw1R1xDp8a+kuPM6"
       
   122     "Gx8S4LXtCIwNO1asWDGYl5dn3gneunGLc7/+gTttoAntQRrTmgMmpimAHQwGOycnlBaX4rUz"
       
   123     "8LszMRweXLr7kWB35oMdCAT+1jRt0cqVK6Otra2+hvoGGuobWPLEEsoXzkbPkLhvR4CBRwJY"
       
   124     "Xq/3SGVlZbq7u7utsrJyxDTNz06cOJHZ0tRCS1MLAKuRwNQT9v8AyV27dn1fXl7eqmlae11d"
       
   125     "XXLfvn0/+Xy+l6LR6Gu2befFYjFfzrk2FzeHp7mK7jdxz2/LffGamhpvc3NzyLKsbFd3z1PG"
       
   126     "aHyBTKdjum0POGzbFAp7qo0xVOtJZdf/C/wRDnL5FYGSAAAAAElFTkSuQmCC")
       
   127 
       
   128 def usage():
    27 def usage():
   129     print """
    28     print """
   130 Usage of Beremiz PLC execution service :\n
    29 Usage of Beremiz PLC execution service :\n
   131 %s {[-n name] [-i ip] [-p port]|-h|--help} working_dir
    30 %s {[-n name] [-i ip] [-p port] [-x enabletaskbar]|-h|--help} working_dir
   132            -n        - zeroconf service name
    31            -n        - zeroconf service name
   133            -i        - ip of interface to bind to (x.x.x.x)
    32            -i        - ip of interface to bind to (x.x.x.x)
   134            -p        - port number
    33            -p        - port number
   135            -h        - print this help text and quit
    34            -h        - print this help text and quit
       
    35            -x        - enable/disable wxTaskbarIcon (0:disable 1:enable)
   136            
    36            
   137            working_dir - directory where are stored PLC files
    37            working_dir - directory where are stored PLC files
   138 """%sys.argv[0]
    38 """%sys.argv[0]
   139 
    39 
   140 try:
    40 try:
   141     opts, args = getopt.getopt(sys.argv[1:], "i:p:n:h")
    41     opts, args = getopt.getopt(sys.argv[1:], "i:p:n:x:h")
   142 except getopt.GetoptError, err:
    42 except getopt.GetoptError, err:
   143     # print help information and exit:
    43     # print help information and exit:
   144     print str(err) # will print something like "option -a not recognized"
    44     print str(err) # will print something like "option -a not recognized"
   145     usage()
    45     usage()
   146     sys.exit(2)
    46     sys.exit(2)
   150 port = 3000
    50 port = 3000
   151 name = os.environ[{
    51 name = os.environ[{
   152      "linux2":"USER",
    52      "linux2":"USER",
   153      "win32":"USERNAME",
    53      "win32":"USERNAME",
   154      }.get(sys.platform, "USER")]
    54      }.get(sys.platform, "USER")]
       
    55 enablewx = True
       
    56 havewx = False
   155 
    57 
   156 for o, a in opts:
    58 for o, a in opts:
   157     if o == "-h":
    59     if o == "-h":
   158         usage()
    60         usage()
   159         sys.exit()
    61         sys.exit()
   163     elif o == "-p":
    65     elif o == "-p":
   164         # port: port that the service runs on
    66         # port: port that the service runs on
   165         port = int(a)
    67         port = int(a)
   166     elif o == "-n":
    68     elif o == "-n":
   167         name = a
    69         name = a
       
    70     elif o == "-x":
       
    71         enablewx = int(a)
   168     else:
    72     else:
   169         usage()
    73         usage()
   170         sys.exit()
    74         sys.exit()
   171 
    75 
   172 if len(args) > 1:
    76 if len(args) > 1:
   176     WorkingDir = args[0]
    80     WorkingDir = args[0]
   177 elif len(args) == 0:
    81 elif len(args) == 0:
   178     WorkingDir = os.getcwd()
    82     WorkingDir = os.getcwd()
   179     args=[WorkingDir]
    83     args=[WorkingDir]
   180 
    84 
       
    85 if enablewx:
       
    86     try:
       
    87         import wx, re
       
    88         from wx.lib.embeddedimage import PyEmbeddedImage
       
    89         from threading import Thread
       
    90         from types import *
       
    91         havewx = True
       
    92     except:
       
    93         havewx = False
       
    94 
       
    95     if havewx:
       
    96         defaulticon = PyEmbeddedImage(
       
    97         "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgIfAhkiAAABc5J"
       
    98         "REFUSIl9lW1MW9cZx3/n2vf6BQO2MZiXGBISILCVUEUlitYpjaKpXZJ1XZZ2kzJVY9r6IeLD"
       
    99         "pGTaNG3KtGmNNGlbpW3VFhRp0l6aZCllpVUqtVNJtBFKE5QXLxCjpCYEY7DBr9hcm3vPPgQY"
       
   100         "IQmPdKR7/vd5/v/n5dxzhZSSNeYBOoGDQGcoFPINDAyUDQ0NOUdGRmyGYSiBQGCpoaGhuGnT"
       
   101         "psShQ4f6WltbewEBVAK3gCBgrjJKKZFSKlLKeillt5Ty40gkMnnw4MFFQG60ysrKZHd3dyoe"
       
   102         "j//bNM0Le/fuPd/e3r5lmRMpJWK5ghrgFeBIT09P4/Hjx73pdFo47HaaNlfRutnJru0OKsoE"
       
   103         "E3GVqaSNa6EUw1dvIKWkoqKCrVu3FoeHh9WamppfRiKRn6wUYAUcwE7g2e7u7vrTp09XGIZB"
       
   104         "W1Mdv3qtmoBPrG0hHVsMhKLj6nqOqOWn/Pjnv2dgYIC5uTl1uSM71/pbgUbg6bNnz/rPnDnj"
       
   105         "dzoddO0P8Oo+jY2suDDD1Zv9DA1dfghXVbVBCFEqpcwAKEDTxMSE58SJE8+oqsq3nq/l1X0a"
       
   106         "QihYtNLHLqRET03wuYp7fO9r26mpKlsVUBSl0W63V6/shZTyyIEDB344Njb21JYaG7/5bgkA"
       
   107         "Dm8zTS/+7bHZLy0mSN+7yNztt8nPjYHFwfvXDf1P70zZ0ok0LS0tZy9fvvxNAGswGFQnJyef"
       
   108         "KnM5+NHLzuUDsrFZ7R68zS/hrGon1PcNMPI0BIzs9tcCNvNfDqxW64uqqvqKxWJc6e3trVVV"
       
   109         "leaAk6ryJ5N/9tH3GXv7Je7/5xermN3diMPXCkDfgrkg3UU0txWLxeLw+/1fB1BGR0frbTYb"
       
   110         "TXWWDbNeysUoZKbIRIZBPviOzKU8ejLMHyPFcMprrweQ7iUAXC7XPiGEak2lUk02m42mWn1D"
       
   111         "gfrnTiKNIrbyzSAUjEKWCx+/Mf+HyELBrLBvBhAIKDdgGsrLy+sAv1UIUa1pGv7yxQ0FbGX1"
       
   112         "D+0LQmHW7fVavE5Mo/gAFCCcoOs6NpvNA7gVRVGCmqYRz1hXg7NFU39rjshawjcuvs4P+o/y"
       
   113         "24uvE1+I4VCdfGfXUb76+VdWfQQCkbJSKBQoFApJTdMsCvApQDSlAjCTN7I/y5CNllpq1wqE"
       
   114         "YmPciIzwwdi7BKevreK7Gp5dfbYoFoozJrquo+v6rMViWbQCV4QQzGTsQJY3kzIhvFpgfYte"
       
   115         "7jhCMp9kk7uep+ueWcWj6f8Xqioq8ck0xcIS6XT6vpRy3gqMqKpqRBfKLLNF1ZRV6YBiPDrw"
       
   116         "vduefwTL6hl6b74FgFVR0T4rJTU3jcvlymcymal8Ph+z9vf3p7u6uv5y/vz5bw994ld2fmUH"
       
   117         "7nYFRVG4Gb3Guv8FpmmQzCcIJ+5w8c5HRFL3UYRC+ZKX633j6LpObW3tDcMwrsODq4Jbt27V"
       
   118         "HT58+N7o6KgCYHfY2f2lXfi+6CJbnsAwjUeyXzFFKLgdHqb+mmL8xh22bduWmJycfHN2dvbX"
       
   119         "uVwuoQC0tbXlKisrYytBi/lFZsKzOErtTyQWCOxWO36ljvl/FLk+dJOSkhJTUZR35+fn+3K5"
       
   120         "XAIeXNcASz6fbxzwrxDYVQdqpARvs498IYchDUxpogiBVVFxqE7U/5Zx4c8fEo/FKS0tlR0d"
       
   121         "HZ8ODg6+l06nr6zwrAp4PJ6Qpmlf2L9/fywYDFaOXB0RI1dHaGpuoq29Fa1Uxe62YeZMInei"
       
   122         "jAY/IRqNAtDZ2blUV1fXPzg4+F5VVdU/H6p0eYjqsWPHvnz37t0XwuHw7d27d4eTyeTvLl26"
       
   123         "FJiamnpim6qrq9mzZ094fHz875FI5J3p6ekr631WBARgaWlpCezYsePeuXPnzFAo5Dp58uS+"
       
   124         "dDp91GKxNBYKBW82m3Vomqa7XK7pbDYbnJmZuR2LxYL5fP79WCyWeeys1h/D9e97enqsp06d"
       
   125         "8mWzWU+xWPTkcjmXaZpxwzDCsVhsbqNggP8BMJOU3UUUf+0AAAAASUVORK5CYII=")
       
   126         
       
   127         #----------------------------------------------------------------------
       
   128         starticon = PyEmbeddedImage(
       
   129         "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgIfAhkiAAABbpJ"
       
   130         "REFUSIl9lltsFNcdxn9nZnbHs15fd23j9TXYC0UCKzEhMQ+oIS2g1kQ1pbFStX0opFWsovSh"
       
   131         "rUqp2pS2ioTUolaKFOGHqGkiJcKRuDhOaZRiZCsCXyBgCBBfMfbu+oa9s17wzuzl9MH24mDD"
       
   132         "XzoPc/6fft+c72jOGSGlZEVlAU8D9cB20zQ9HR0duRcvXszq7e01EomEUlFREa+srLR8Pl+g"
       
   133         "sbHx3zk5ORcAFfACA8Bt4CFUSomUUkgpS6SUB6SUH5umOXLgwIEHqqrKJfGao7S0VB49ejRo"
       
   134         "2/YnUsrT+/fvb66pqSldYiKlRCytoBB4Gfjx6dOnq5qamjwTExOKqqqU+QrYUJFN7QY32Qbc"
       
   135         "vSeYCGtcux1i5M5dAPx+P1VVVQvnzp0ziouLfx8MBt9cXoAGZABbgZ1HjhwpO378eEEymaSi"
       
   136         "tIBjPy9lU5nKoyWExF2yjy+mN3HsH+/Q3d3NwMCAsZTI9pVaDXgK2Hr27Nn85ubmEpdh8IMX"
       
   137         "ffxirwshVrGXHBQSC/dIRvoZGuz/WkvTtHIhhCGlXABQgI2Tk5P5hw8f3uZwOGj8VjGHXnoC"
       
   138         "HJCpJFbkLtr8FXbX+XC79HRPVVW/qqre9LtIKX/S0NDwy76+vq1lhTr/fM2NAmTk+fHv/dea"
       
   139         "BlZkDHP0PHODH2NHg1gykw8/X7Dfb7vjTNgJqqurT3R1db0GoF2/fl0fGhqqdWca/K7RhZLO"
       
   140         "WSBU55oGGXlVZORVkeV7nsFPDqKL+9TWJCI3n9rojX2mYhjGj4QQv5FSziunTp0qdjqd4hvl"
       
   141         "Lnz5j49lrPMNhv7zM6b63knPuQpryMj3A9A2L++nvDaZXheqqrrXrVu3D0C5detWudPpxO/T"
       
   142         "Hk8HYnOD3J+8yr3bH6XnZNImHg3xfsgenfHo5QAyJwFAdnb2HiGEppmmWa3rOhtKrCcalNT9"
       
   143         "llTSwvBsXISn4nRdbJ5/czRsWvlGhQAEYtFg0kl2dnYZUKgB5U6nk5L82BMNXIU1X3uOWFH5"
       
   144         "eWIuy/YYWcjU4qQAxQ22bWMYhgfIU1RV/UrXdWaiDyOyUiLROktoJfDtC8fZfWQbb//v75ix"
       
   145         "MDlGnvjVC3+gflNDWiMQKPMalmVh2/a8w+HQFKAHIBR2ABCOS+uN6cTMoFstXmlwZbSba7tv"
       
   146         "8hfzT7z+7k+ZnZ0BoK5yR1qjCBV7MoVt29i2PaWqqq0BvUIIQqYORHlrKj6R9BoVj0b04oY9"
       
   147         "nEt+yvz3Y5yR/+Xap3XsDb/EtvV1aY1DdTA7HsW2bCKRyLiUclYBelRVldNWAfPSm4oV5ZQJ"
       
   148         "Vn/G9Zv2oWt6Ous7e4K81XiC1wNNBO6OIWKgB7Mwp000TYuFw+GxWCw2qbS2tk7k5uae/eDD"
       
   149         "Fn594p6SFyxRCjKLUBWF8fBoegTNMVLLm/kwdMyGGON/nePLklv0dl/Cii3gdrtvAzdg8aig"
       
   150         "vb296uDBgwMjIyMCwFvoZXv9NvRnIKqHSckUyQdJrtfexPqm5LGVAuNdVaofcCVywfpexLYD"
       
   151         "CsDOnTvnioqKzGXdzNQMV9tvkJEyUITyeOAjpYyAc9gxYc/GWyK2HYDF4xog6fV6h1i8FwCo"
       
   152         "LK/EncwhkWGxEH9AXLMXM2H1CpQBifI3yeapZ+70d43+cSo4+95yL23g8XiGFUWp3bVrV/Ty"
       
   153         "5ctZnR2ddHZ08uxzz1K9eT1GRhJls1gFlsfieK+WpJ5e/3z7pcuXzmia1rJSs3xlOg8dOvTD"
       
   154         "8fHx7wQCgb4tW7bMm6b55/Pnz+eGw+FFGJDT5iT1XRWlfxHMZ06+/Vz9dCAQeG9kZKR1x44d"
       
   155         "nSdPnkyuZSAArbq6eqOiKAP9/f3xlpaWgra2tlei0eiryWSyKGKa2TcaL+muwcxU5aDf9Gi+"
       
   156         "L0Oh0BehUOiaZVlnAoHAzFr7Ih75bVnVb2pqcvf09Phi0ei6+/rUC6lw1k0p5bSUctThcIwP"
       
   157         "Dw/HnwT4P6CDl+TMvD0JAAAAAElFTkSuQmCC")
       
   158         
       
   159         #----------------------------------------------------------------------
       
   160         stopicon = PyEmbeddedImage(
       
   161         "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgIfAhkiAAABPRJ"
       
   162         "REFUSImdlllsVGUUx3/f/e4sd5iZLjNt6XSFdtgkjWFRePABDaCBGgjamIg81CU0aoxbRHww"
       
   163         "+EDkhWjEB5rYGEMUxQTCJg8EoQ2BbgrFCNJWltplgC63naEzd+bO50NLLVAq4STfwz3nfP/f"
       
   164         "PSf3O98VSikmmQ94HFgDLDdNM1BfX5955swZX0tLi5FKpbSSkpJkaWlpIhQKdVdVVX2XkZFx"
       
   165         "EpBAEGgHLgH/iSqlUEoJpVSBUqpaKXXYNM0r1dXVt6WUajx5ylVYWKi2bdvWY1nWUaXUgQ0b"
       
   166         "NtRWVFQUjmuilEKMV5ALvAhsPHDgQFlNTU2gr69Pk1JSFMphTomfRXO8+A243i/oG9I5f6mX"
       
   167         "K1evAxAOhykrKxs9duyYkZ+f/0lPT8/2OwXogBtYDKzYunVr0c6dO3Ns26akMIcdbxQyv0hy"
       
   168         "rwmh8Bas5/eb89nxRR1NTU20t7cb4x1ZPjlXB2YBiw8ePJhdW1tb4DEMXng6xJtrPQhxn/Y4"
       
   169         "QSM12o89fJnOjst3hXRdLxZCGEqpUQANmBuJRLK3bNmy1OFwUPVMPm9VTiMOqLRNYvg6+shv"
       
   170         "rFoWwutxTcSklGEpZXDiXZRSr6xbt+6dtra2xUW5Lr7c7EUD3Flhwmu/nRKQGO7CvHaCwY7D"
       
   171         "WNEeEmoGe0+PWnuOXHWmrBTl5eW7GxsbNwPoFy5ccHV2di7yzjD4uMqDNtFngZDOKQHurDLc"
       
   172         "WWX4Qk/ScfRVXCLGoorU8J+z5gbjxyWGYbwshPhQKTWi7d+/P9/pdIp5xR5C2Q9uS1fDp3T+"
       
   173         "8jo32uomfJ7cCtzZYQCOjKhYOmgxI+hBSumdOXPmegDt4sWLxU6nk3BIf7A6EB/sIBY5R/+l"
       
   174         "nyd8yrZIRnvZ02tduxVwFQOojBQAfr9/tRBC103TLHe5XMwpSEwLKFj2EWk7gRGYOyaeTtJ4"
       
   175         "pnZk+7UhM5FtlAhAIMYAESd+v78IyNWBYqfTSUF2fFqAJ7firufhRFSdTg36rIDhQ6XHnAI0"
       
   176         "L1iWhWEYASBLl1L+JaWcfSuqk+u3AUikRer4ADffg/w7gt80fs35r34k3BYh2xNAarooAJ4d"
       
   177         "vsHgaP8EWMR17GiaVo8r0+Fw6DrQDDzXO+RgQSjBUFIlPh+wB0vLZD6TrLWrkWRXB29fGAK6"
       
   178         "pql1rNXVmrCklJYGtAgh6DXHDsuuG8k+O9M5895tq+atpSwwZ9o2TjZlWTGl1IAGNEsp1c1E"
       
   179         "DiMqmI7nZRQJ7j/G6xZWMS/vsYcGkEzG4vF4RDt06FBfZmbmwR/27uOD3f1aVk+BljMjD6lp"
       
   180         "/DN07a4VTYw8tL4rrQZgbNixadOm90+dOvX82cZmcbaxmWBukOVrlvJudw1R1xDp8a+kuPM6"
       
   181         "Gx8S4LXtCIwNO1asWDGYl5dn3gneunGLc7/+gTttoAntQRrTmgMmpimAHQwGOycnlBaX4rUz"
       
   182         "8LszMRweXLr7kWB35oMdCAT+1jRt0cqVK6Otra2+hvoGGuobWPLEEsoXzkbPkLhvR4CBRwJY"
       
   183         "Xq/3SGVlZbq7u7utsrJyxDTNz06cOJHZ0tRCS1MLAKuRwNQT9v8AyV27dn1fXl7eqmlae11d"
       
   184         "XXLfvn0/+Xy+l6LR6Gu2befFYjFfzrk2FzeHp7mK7jdxz2/LffGamhpvc3NzyLKsbFd3z1PG"
       
   185         "aHyBTKdjum0POGzbFAp7qo0xVOtJZdf/C/wRDnL5FYGSAAAAAElFTkSuQmCC")
       
   186         
       
   187     class ParamsEntryDialog(wx.TextEntryDialog):
       
   188         if wx.VERSION < (2, 6, 0):
       
   189             def Bind(self, event, function, id = None):
       
   190                 if id is not None:
       
   191                     event(self, id, function)
       
   192                 else:
       
   193                     event(self, function)
       
   194         
       
   195     
       
   196         def __init__(self, parent, message, caption = "Please enter text", defaultValue = "", 
       
   197                            style = wx.OK|wx.CANCEL|wx.CENTRE, pos = wx.DefaultPosition):
       
   198             wx.TextEntryDialog.__init__(self, parent, message, caption, defaultValue, style, pos)
       
   199             
       
   200             self.Tests = []
       
   201             if wx.VERSION >= (2, 8, 0):
       
   202                 self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.GetAffirmativeId())
       
   203             elif wx.VERSION >= (2, 6, 0):
       
   204                 self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.GetSizer().GetItem(3).GetSizer().GetAffirmativeButton().GetId())
       
   205             else:
       
   206                 self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.GetSizer().GetItem(3).GetSizer().GetChildren()[0].GetSizer().GetChildren()[0].GetWindow().GetId())
       
   207         
       
   208         def OnOK(self, event):
       
   209             value = self.GetValue()
       
   210             texts = {"value" : value}
       
   211             for function, message in self.Tests:
       
   212                 if not function(value):
       
   213                     message = wx.MessageDialog(self, message%texts, "Error", wx.OK|wx.ICON_ERROR)
       
   214                     message.ShowModal()
       
   215                     message.Destroy()
       
   216                     return
       
   217             self.EndModal(wx.ID_OK)
       
   218         
       
   219         def GetValue(self):
       
   220             return self.GetSizer().GetItem(1).GetWindow().GetValue()
       
   221         
       
   222         def SetTests(self, tests):
       
   223             self.Tests = tests
       
   224             
       
   225     class DemoTaskBarIcon(wx.TaskBarIcon):
       
   226         TBMENU_CHANGE_NAME = wx.NewId()
       
   227         TBMENU_CHANGE_PORT = wx.NewId()
       
   228         TBMENU_CHANGE_INTERFACE = wx.NewId()
       
   229         TBMENU_CHANGE_WD = wx.NewId()
       
   230         TBMENU_QUIT = wx.NewId()
       
   231         
       
   232         def __init__(self, pyroserver):
       
   233             wx.TaskBarIcon.__init__(self)
       
   234             # Set the image
       
   235             self.UpdateIcon(None)
       
   236     
       
   237             # bind some events
       
   238             self.Bind(wx.EVT_MENU, self.OnTaskBarChangeName, id=self.TBMENU_CHANGE_NAME)
       
   239             self.Bind(wx.EVT_MENU, self.OnTaskBarChangeInterface, id=self.TBMENU_CHANGE_INTERFACE)
       
   240             self.Bind(wx.EVT_MENU, self.OnTaskBarChangePort, id=self.TBMENU_CHANGE_PORT)
       
   241             self.Bind(wx.EVT_MENU, self.OnTaskBarChangeWorkingDir, id=self.TBMENU_CHANGE_WD)
       
   242             self.Bind(wx.EVT_MENU, self.OnTaskBarQuit, id=self.TBMENU_QUIT)
       
   243         
       
   244         def CreatePopupMenu(self):
       
   245             """
       
   246             This method is called by the base class when it needs to popup
       
   247             the menu for the default EVT_RIGHT_DOWN event.  Just create
       
   248             the menu how you want it and return it from this function,
       
   249             the base class takes care of the rest.
       
   250             """
       
   251             menu = wx.Menu()
       
   252             menu.Append(self.TBMENU_CHANGE_NAME, "Change Name")
       
   253             menu.Append(self.TBMENU_CHANGE_INTERFACE, "Change IP of interface to bind")
       
   254             menu.Append(self.TBMENU_CHANGE_PORT, "Change Port Number")
       
   255             menu.AppendSeparator()
       
   256             menu.Append(self.TBMENU_CHANGE_WD, "Change working directory")
       
   257             menu.Append(self.TBMENU_QUIT, "Quit")
       
   258             return menu
       
   259     
       
   260         def MakeIcon(self, img):
       
   261             """
       
   262             The various platforms have different requirements for the
       
   263             icon size...
       
   264             """
       
   265             if "wxMSW" in wx.PlatformInfo:
       
   266                 img = img.Scale(16, 16)
       
   267             elif "wxGTK" in wx.PlatformInfo:
       
   268                 img = img.Scale(22, 22)
       
   269             # wxMac can be any size upto 128x128, so leave the source img alone....
       
   270             icon = wx.IconFromBitmap(img.ConvertToBitmap() )
       
   271             return icon
       
   272         
       
   273         def OnTaskBarChangeInterface(self,evt):
       
   274             dlg = ParamsEntryDialog(None, "Enter the ip of the interface to bind", defaultValue=pyroserver.ip)
       
   275             dlg.SetTests([(re.compile('\d{1,3}(?:\.\d{1,3}){3}$').match, "Ip is not valid!"),
       
   276                            ( lambda ip :len([x for x in ip.split(".") if 0 <= int(x) <= 255]) == 4, "Ip is not valid!")
       
   277                            ])
       
   278             if dlg.ShowModal() == wx.ID_OK:
       
   279                 pyroserver.ip = dlg.GetValue()
       
   280                 pyroserver.Stop()
       
   281                 
       
   282         def OnTaskBarChangePort(self,evt):
       
   283             dlg = ParamsEntryDialog(None, "Enter a port number ", defaultValue=str(pyroserver.port))
       
   284             dlg.SetTests([(UnicodeType.isdigit, "Port number must be an integer!"), (lambda port : 0 <= int(port) <= 65535 , "Port number must be 0 <= port <= 65535!")])
       
   285             if dlg.ShowModal() == wx.ID_OK:
       
   286                 pyroserver.port = int(dlg.GetValue())
       
   287                 pyroserver.Stop()
       
   288                 
       
   289         
       
   290         def OnTaskBarChangeWorkingDir(self,evt):
       
   291             dlg = wx.DirDialog(None, "Choose a working directory ", pyroserver.workdir, wx.DD_NEW_DIR_BUTTON)
       
   292             if dlg.ShowModal() == wx.ID_OK:
       
   293                 pyroserver.workdir = dlg.GetPath()
       
   294                 pyroserver.Stop()
       
   295                 
       
   296         def OnTaskBarChangeName(self,evt):
       
   297             dlg = ParamsEntryDialog(None, "Enter a name ", defaultValue=pyroserver.name)
       
   298             dlg.SetTests([(lambda name : len(name) is not 0 , "Name must not be null!")])
       
   299             if dlg.ShowModal() == wx.ID_OK:
       
   300                 pyroserver.name = dlg.GetValue()
       
   301                 pyroserver.Restart()
       
   302     
       
   303         def OnTaskBarQuit(self,evt):
       
   304             pyroserver.Quit()
       
   305             self.RemoveIcon()
       
   306             wx.GetApp().ExitMainLoop()
       
   307             
       
   308         def UpdateIcon(self, plcstatus):
       
   309             if plcstatus is "Started" :
       
   310                 currenticon = self.MakeIcon(starticon.GetImage())
       
   311             elif plcstatus is "Stopped":
       
   312                 currenticon = self.MakeIcon(stopicon.GetImage())
       
   313             else:
       
   314                 currenticon = self.MakeIcon(defaulticon.GetImage())
       
   315             self.SetIcon(currenticon, "Beremiz Service")
       
   316 
   181 from runtime import PLCObject, ServicePublisher
   317 from runtime import PLCObject, ServicePublisher
   182 import Pyro.core as pyro
   318 import Pyro.core as pyro
   183 
   319 
   184 if not os.path.isdir(WorkingDir):
   320 if not os.path.isdir(WorkingDir):
   185     os.mkdir(WorkingDir)
   321     os.mkdir(WorkingDir)
   186 
       
   187 
       
   188 
   322 
   189 class Server():
   323 class Server():
   190     def __init__(self, name, ip, port, workdir, args):
   324     def __init__(self, name, ip, port, workdir, args):
   191         self.continueloop = True
   325         self.continueloop = True
   192         self.daemon = None
   326         self.daemon = None
   235     def Stop(self):
   369     def Stop(self):
   236         if self.servicepublisher is not None:
   370         if self.servicepublisher is not None:
   237             self.servicepublisher.UnRegisterService()
   371             self.servicepublisher.UnRegisterService()
   238             del self.servicepublisher
   372             del self.servicepublisher
   239         self.daemon.shutdown(True)
   373         self.daemon.shutdown(True)
   240 
       
   241 class ParamsEntryDialog(wx.TextEntryDialog):
       
   242     if wx.VERSION < (2, 6, 0):
       
   243         def Bind(self, event, function, id = None):
       
   244             if id is not None:
       
   245                 event(self, id, function)
       
   246             else:
       
   247                 event(self, function)
       
   248     
       
   249 
       
   250     def __init__(self, parent, message, caption = "Please enter text", defaultValue = "", 
       
   251                        style = wx.OK|wx.CANCEL|wx.CENTRE, pos = wx.DefaultPosition):
       
   252         wx.TextEntryDialog.__init__(self, parent, message, caption, defaultValue, style, pos)
       
   253         
       
   254         self.Tests = []
       
   255         if wx.VERSION >= (2, 8, 0):
       
   256             self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.GetAffirmativeId())
       
   257         elif wx.VERSION >= (2, 6, 0):
       
   258             self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.GetSizer().GetItem(3).GetSizer().GetAffirmativeButton().GetId())
       
   259         else:
       
   260             self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.GetSizer().GetItem(3).GetSizer().GetChildren()[0].GetSizer().GetChildren()[0].GetWindow().GetId())
       
   261     
       
   262     def OnOK(self, event):
       
   263         value = self.GetValue()
       
   264         texts = {"value" : value}
       
   265         for function, message in self.Tests:
       
   266             if not function(value):
       
   267                 message = wx.MessageDialog(self, message%texts, "Error", wx.OK|wx.ICON_ERROR)
       
   268                 message.ShowModal()
       
   269                 message.Destroy()
       
   270                 return
       
   271         self.EndModal(wx.ID_OK)
       
   272     
       
   273     def GetValue(self):
       
   274         return self.GetSizer().GetItem(1).GetWindow().GetValue()
       
   275     
       
   276     def SetTests(self, tests):
       
   277         self.Tests = tests
       
   278         
       
   279 class DemoTaskBarIcon(wx.TaskBarIcon):
       
   280     TBMENU_CHANGE_NAME = wx.NewId()
       
   281     TBMENU_CHANGE_PORT = wx.NewId()
       
   282     TBMENU_CHANGE_INTERFACE = wx.NewId()
       
   283     TBMENU_CHANGE_WD = wx.NewId()
       
   284     TBMENU_QUIT = wx.NewId()
       
   285     
       
   286     def __init__(self, pyroserver):
       
   287         wx.TaskBarIcon.__init__(self)
       
   288         # Set the image
       
   289         self.UpdateIcon(None)
       
   290 
       
   291         # bind some events
       
   292         self.Bind(wx.EVT_MENU, self.OnTaskBarChangeName, id=self.TBMENU_CHANGE_NAME)
       
   293         self.Bind(wx.EVT_MENU, self.OnTaskBarChangeInterface, id=self.TBMENU_CHANGE_INTERFACE)
       
   294         self.Bind(wx.EVT_MENU, self.OnTaskBarChangePort, id=self.TBMENU_CHANGE_PORT)
       
   295         self.Bind(wx.EVT_MENU, self.OnTaskBarChangeWorkingDir, id=self.TBMENU_CHANGE_WD)
       
   296         self.Bind(wx.EVT_MENU, self.OnTaskBarQuit, id=self.TBMENU_QUIT)
       
   297     
       
   298     def CreatePopupMenu(self):
       
   299         """
       
   300         This method is called by the base class when it needs to popup
       
   301         the menu for the default EVT_RIGHT_DOWN event.  Just create
       
   302         the menu how you want it and return it from this function,
       
   303         the base class takes care of the rest.
       
   304         """
       
   305         menu = wx.Menu()
       
   306         menu.Append(self.TBMENU_CHANGE_NAME, "Change Name")
       
   307         menu.Append(self.TBMENU_CHANGE_INTERFACE, "Change IP of interface to bind")
       
   308         menu.Append(self.TBMENU_CHANGE_PORT, "Change Port Number")
       
   309         menu.AppendSeparator()
       
   310         menu.Append(self.TBMENU_CHANGE_WD, "Change working directory")
       
   311         menu.Append(self.TBMENU_QUIT, "Quit")
       
   312         return menu
       
   313 
       
   314     def MakeIcon(self, img):
       
   315         """
       
   316         The various platforms have different requirements for the
       
   317         icon size...
       
   318         """
       
   319         if "wxMSW" in wx.PlatformInfo:
       
   320             img = img.Scale(16, 16)
       
   321         elif "wxGTK" in wx.PlatformInfo:
       
   322             img = img.Scale(22, 22)
       
   323         # wxMac can be any size upto 128x128, so leave the source img alone....
       
   324         icon = wx.IconFromBitmap(img.ConvertToBitmap() )
       
   325         return icon
       
   326     
       
   327     def OnTaskBarChangeInterface(self,evt):
       
   328         dlg = ParamsEntryDialog(None, "Enter the ip of the interface to bind", defaultValue=pyroserver.ip)
       
   329         dlg.SetTests([(re.compile('\d{1,3}(?:\.\d{1,3}){3}$').match, "Ip is not valid!"),
       
   330                        ( lambda ip :len([x for x in ip.split(".") if 0 <= int(x) <= 255]) == 4, "Ip is not valid!")
       
   331                        ])
       
   332         if dlg.ShowModal() == wx.ID_OK:
       
   333             pyroserver.ip = dlg.GetValue()
       
   334             pyroserver.Stop()
       
   335             
       
   336     def OnTaskBarChangePort(self,evt):
       
   337         dlg = ParamsEntryDialog(None, "Enter a port number ", defaultValue=str(pyroserver.port))
       
   338         dlg.SetTests([(UnicodeType.isdigit, "Port number must be an integer!"), (lambda port : 0 <= int(port) <= 65535 , "Port number must be 0 <= port <= 65535!")])
       
   339         if dlg.ShowModal() == wx.ID_OK:
       
   340             pyroserver.port = int(dlg.GetValue())
       
   341             pyroserver.Stop()
       
   342             
       
   343     
       
   344     def OnTaskBarChangeWorkingDir(self,evt):
       
   345         dlg = wx.DirDialog(None, "Choose a working directory ", pyroserver.workdir, wx.DD_NEW_DIR_BUTTON)
       
   346         if dlg.ShowModal() == wx.ID_OK:
       
   347             pyroserver.workdir = dlg.GetPath()
       
   348             pyroserver.Stop()
       
   349             
       
   350     def OnTaskBarChangeName(self,evt):
       
   351         dlg = ParamsEntryDialog(None, "Enter a name ", defaultValue=pyroserver.name)
       
   352         dlg.SetTests([(lambda name : len(name) is not 0 , "Name must not be null!")])
       
   353         if dlg.ShowModal() == wx.ID_OK:
       
   354             pyroserver.name = dlg.GetValue()
       
   355             pyroserver.Restart()
       
   356 
       
   357     def OnTaskBarQuit(self,evt):
       
   358         pyroserver.Quit()
       
   359         self.RemoveIcon()
       
   360         wx.GetApp().ExitMainLoop()
       
   361         
       
   362     def UpdateIcon(self, plcstatus):
       
   363         if plcstatus is "Started" :
       
   364             currenticon = self.MakeIcon(starticon.GetImage())
       
   365         elif plcstatus is "Stopped":
       
   366             currenticon = self.MakeIcon(stopicon.GetImage())
       
   367         else:
       
   368             currenticon = self.MakeIcon(defaulticon.GetImage())
       
   369         self.SetIcon(currenticon, "Beremiz Service")
       
   370         
   374         
   371 pyroserver = Server(name, ip, port, WorkingDir, args)
   375 pyroserver = Server(name, ip, port, WorkingDir, args)
   372 
   376 
   373 if havewx:
   377 if havewx:
   374     app=wx.App(redirect=False)
   378     app=wx.App(redirect=False)