~ubuntu-branches/ubuntu/vivid/pymodbus/vivid

« back to all changes in this revision

Viewing changes to examples/gui/wx/simulator.py

  • Committer: Package Import Robot
  • Author(s): W. Martin Borgert, jwilk at debian, W. Martin Borgert
  • Date: 2013-05-17 16:21:29 UTC
  • mfrom: (5.1.2 experimental)
  • Revision ID: package-import@ubuntu.com-20130517162129-7qs3ggxt1b2dfwys
Tags: 1.2.0-2
[Jakub Wilk <jwilk@debian.org>  Sun, 05 May 2013 16:01:51 +0200]
* Use canonical URIs for Vcs-* fields.
[W. Martin Borgert <debacle@debian.org>]
* upload to unstable

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#!/usr/bin/env python
2
 
'''
3
 
Note that this is not finished
4
 
'''
5
 
#---------------------------------------------------------------------------#
6
 
# System
7
 
#---------------------------------------------------------------------------#
8
 
import os
9
 
import getpass
10
 
import pickle
11
 
from threading import Thread
12
 
 
13
 
#---------------------------------------------------------------------------#
14
 
# For Gui
15
 
#---------------------------------------------------------------------------#
16
 
import wx
17
 
from twisted.internet import wxreactor
18
 
wxreactor.install()
19
 
 
20
 
#---------------------------------------------------------------------------#
21
 
# SNMP Simulator
22
 
#---------------------------------------------------------------------------#
23
 
from twisted.internet import reactor
24
 
from twisted.internet import error as twisted_error
25
 
from pymodbus.server.async import ModbusServerFactory
26
 
from pymodbus.datastore import ModbusServerContext,ModbusSlaveContext
27
 
 
28
 
#--------------------------------------------------------------------------#
29
 
# Logging
30
 
#--------------------------------------------------------------------------#
31
 
import logging
32
 
log = logging.getLogger(__name__)
33
 
 
34
 
#---------------------------------------------------------------------------#
35
 
# Application Error
36
 
#---------------------------------------------------------------------------#
37
 
class ConfigurationException(Exception):
38
 
    ''' Exception for configuration error '''
39
 
    pass
40
 
 
41
 
#---------------------------------------------------------------------------#
42
 
# Extra Global Functions
43
 
#---------------------------------------------------------------------------#
44
 
# These are extra helper functions that don't belong in a class
45
 
#---------------------------------------------------------------------------#
46
 
def root_test():
47
 
    ''' Simple test to see if we are running as root '''
48
 
    return getpass.getuser() == "root"
49
 
 
50
 
#---------------------------------------------------------------------------#
51
 
# Simulator Class
52
 
#---------------------------------------------------------------------------#
53
 
class Simulator(object):
54
 
    '''
55
 
    Class used to parse configuration file and create and modbus
56
 
    datastore.
57
 
 
58
 
    The format of the configuration file is actually just a
59
 
    python pickle, which is a compressed memory dump from
60
 
    the scraper.
61
 
    '''
62
 
 
63
 
    def __init__(self, config):
64
 
        '''
65
 
        Trys to load a configuration file, lets the file not
66
 
        found exception fall through
67
 
 
68
 
        @param config The pickled datastore
69
 
        '''
70
 
        try:
71
 
            self.file = open(config, "r")
72
 
        except Exception:
73
 
            raise ConfigurationException("File not found %s" % config)
74
 
 
75
 
    def _parse(self):
76
 
        ''' Parses the config file and creates a server context '''
77
 
        try:
78
 
            handle = pickle.load(self.file)
79
 
            dsd = handle['di']
80
 
            csd = handle['ci']
81
 
            hsd = handle['hr']
82
 
            isd = handle['ir']
83
 
        except KeyError:
84
 
            raise ConfigurationException("Invalid Configuration")
85
 
        slave = ModbusSlaveContext(d=dsd, c=csd, h=hsd, i=isd)
86
 
        return ModbusServerContext(slaves=slave)
87
 
 
88
 
    def _simulator(self):
89
 
        ''' Starts the snmp simulator '''
90
 
        ports = [502]+range(20000,25000)
91
 
        for port in ports:
92
 
            try:
93
 
                reactor.listenTCP(port, ModbusServerFactory(self._parse()))
94
 
                print 'listening on port', port
95
 
                return port
96
 
            except twisted_error.CannotListenError:
97
 
                pass
98
 
 
99
 
    def run(self):
100
 
        ''' Used to run the simulator '''
101
 
        reactor.callWhenRunning(self._simulator)
102
 
 
103
 
#---------------------------------------------------------------------------#
104
 
# Network reset thread
105
 
#---------------------------------------------------------------------------#
106
 
# This is linux only, maybe I should make a base class that can be filled
107
 
# in for linux(debian/redhat)/windows/nix
108
 
#---------------------------------------------------------------------------#
109
 
class NetworkReset(Thread):
110
 
    '''
111
 
    This class is simply a daemon that is spun off at the end of the
112
 
    program to call the network restart function (an easy way to
113
 
    remove all the virtual interfaces)
114
 
    '''
115
 
    def __init__(self):
116
 
        ''' Initializes a new instance of the network reset thread '''
117
 
        Thread.__init__(self)
118
 
        self.setDaemon(True)
119
 
 
120
 
    def run(self):
121
 
        ''' Run the network reset '''
122
 
        os.system("/etc/init.d/networking restart")
123
 
 
124
 
#---------------------------------------------------------------------------#
125
 
# Main Gui Class
126
 
#---------------------------------------------------------------------------#
127
 
class SimulatorFrame(wx.Frame):
128
 
    '''
129
 
    This class implements the GUI for the flasher application
130
 
    '''
131
 
    subnet = 205
132
 
    number = 1
133
 
    restart = 0
134
 
 
135
 
    def __init__(self, parent, id, title):
136
 
        '''
137
 
        Sets up the gui, callback, and widget handles
138
 
        '''
139
 
        wx.Frame.__init__(self, parent, id, title)
140
 
        wx.EVT_CLOSE(self, self.close_clicked)
141
 
 
142
 
        #---------------------------------------------------------------------------#
143
 
        # Add button row
144
 
        #---------------------------------------------------------------------------#
145
 
        panel = wx.Panel(self, -1)
146
 
        box = wx.BoxSizer(wx.HORIZONTAL)
147
 
        box.Add(wx.Button(panel, 1, 'Apply'), 1)
148
 
        box.Add(wx.Button(panel, 2, 'Help'),  1)
149
 
        box.Add(wx.Button(panel, 3, 'Close'), 1)
150
 
        panel.SetSizer(box)
151
 
 
152
 
        #---------------------------------------------------------------------------#
153
 
        # Add input boxes
154
 
        #---------------------------------------------------------------------------#
155
 
        #self.tdevice    = self.tree.get_widget("fileTxt")
156
 
        #self.tsubnet    = self.tree.get_widget("addressTxt")
157
 
        #self.tnumber    = self.tree.get_widget("deviceTxt")
158
 
 
159
 
        #---------------------------------------------------------------------------#
160
 
        # Tie callbacks
161
 
        #---------------------------------------------------------------------------#
162
 
        self.Bind(wx.EVT_BUTTON, self.start_clicked, id=1)
163
 
        self.Bind(wx.EVT_BUTTON, self.help_clicked,  id=2)
164
 
        self.Bind(wx.EVT_BUTTON, self.close_clicked, id=3)
165
 
 
166
 
        #if not root_test():
167
 
        #    self.error_dialog("This program must be run with root permissions!", True)
168
 
 
169
 
#---------------------------------------------------------------------------#
170
 
# Gui helpers
171
 
#---------------------------------------------------------------------------#
172
 
# Not callbacks, but used by them
173
 
#---------------------------------------------------------------------------#
174
 
    def show_buttons(self, state=False, all=0):
175
 
        ''' Greys out the buttons '''
176
 
        if all:
177
 
            self.window.set_sensitive(state)
178
 
        self.bstart.set_sensitive(state)
179
 
        self.tdevice.set_sensitive(state)
180
 
        self.tsubnet.set_sensitive(state)
181
 
        self.tnumber.set_sensitive(state)
182
 
 
183
 
    def destroy_interfaces(self):
184
 
        ''' This is used to reset the virtual interfaces '''
185
 
        if self.restart:
186
 
            n = NetworkReset()
187
 
            n.start()
188
 
 
189
 
    def error_dialog(self, message, quit=False):
190
 
        ''' Quick pop-up for error messages '''
191
 
        log.debug("error event called")
192
 
        dialog = wx.MessageDialog(self, message, 'Error',
193
 
            wx.OK | wx.ICON_ERROR)
194
 
        dialog.ShowModel()
195
 
        if quit: self.Destroy()
196
 
        dialog.Destroy()
197
 
 
198
 
#---------------------------------------------------------------------------#
199
 
# Button Actions
200
 
#---------------------------------------------------------------------------#
201
 
# These are all callbacks for the various buttons
202
 
#---------------------------------------------------------------------------#
203
 
    def start_clicked(self, widget):
204
 
        ''' Starts the simulator '''
205
 
        start = 1
206
 
        base = "172.16"
207
 
 
208
 
        # check starting network
209
 
        net = self.tsubnet.get_text()
210
 
        octets = net.split('.')
211
 
        if len(octets) == 4:
212
 
            base = "%s.%s" % (octets[0], octets[1])
213
 
            net = int(octets[2]) % 255
214
 
            start = int(octets[3]) % 255
215
 
        else:
216
 
            self.error_dialog("Invalid starting address!");
217
 
            return False
218
 
 
219
 
        # check interface size
220
 
        size = int(self.tnumber.get_text())
221
 
        if (size >= 1):
222
 
            for i in range(start, (size + start)):
223
 
                j = i % 255
224
 
                cmd = "/sbin/ifconfig eth0:%d %s.%d.%d" % (i, base, net, j)
225
 
                os.system(cmd)
226
 
                if j == 254: net = net + 1
227
 
            self.restart = 1
228
 
        else:
229
 
            self.error_dialog("Invalid number of devices!");
230
 
            return False
231
 
 
232
 
        # check input file
233
 
        if os.path.exists(self.file):
234
 
            self.show_buttons(state=False)
235
 
            try:
236
 
                handle = Simulator(config=self.file)
237
 
                handle.run()
238
 
            except ConfigurationException, ex:
239
 
                self.error_dialog("Error %s" % ex)
240
 
                self.show_buttons(state=True)
241
 
        else:
242
 
            self.error_dialog("Device to emulate does not exist!");
243
 
            return False
244
 
 
245
 
    def help_clicked(self, widget):
246
 
        ''' Quick pop-up for about page '''
247
 
        data = gtk.AboutDialog()
248
 
        data.set_version("0.1")
249
 
        data.set_name(('Modbus Simulator'))
250
 
        data.set_authors(["Galen Collins"])
251
 
        data.set_comments(('First Select a device to simulate,\n'
252
 
            + 'then select the starting subnet of the new devices\n'
253
 
            + 'then select the number of device to simulate and click start'))
254
 
        data.set_website("http://code.google.com/p/pymodbus/")
255
 
        data.connect("response", lambda w,r: w.hide())
256
 
        data.run()
257
 
 
258
 
    def close_clicked(self, event):
259
 
        ''' Callback for close button '''
260
 
        log.debug("close event called")
261
 
        reactor.stop()
262
 
 
263
 
    def file_changed(self, event):
264
 
        ''' Callback for the filename change '''
265
 
        self.file = widget.get_filename()
266
 
 
267
 
class SimulatorApp(wx.App):
268
 
    ''' The main wx application handle for our simulator
269
 
    '''
270
 
 
271
 
    def OnInit(self):
272
 
        ''' Called by wxWindows to initialize our application
273
 
 
274
 
        :returns: Always True
275
 
        '''
276
 
        log.debug("application initialize event called")
277
 
        reactor.registerWxApp(self)
278
 
        frame = SimulatorFrame(None, -1, "Pymodbus Simulator")
279
 
        frame.CenterOnScreen()
280
 
        frame.Show(True)
281
 
        self.SetTopWindow(frame)
282
 
        return True
283
 
 
284
 
#---------------------------------------------------------------------------#
285
 
# Main handle function
286
 
#---------------------------------------------------------------------------#
287
 
# This is called when the application is run from a console
288
 
# We simply start the gui and start the twisted event loop
289
 
#---------------------------------------------------------------------------#
290
 
def main():
291
 
    '''
292
 
    Main control function
293
 
    This either launches the gui or runs the command line application
294
 
    '''
295
 
    debug = True
296
 
    if debug:
297
 
        try:
298
 
            log.setLevel(logging.DEBUG)
299
 
            logging.basicConfig()
300
 
        except Exception, e:
301
 
            print "Logging is not supported on this system"
302
 
    simulator = SimulatorApp(0)
303
 
    reactor.run()
304
 
 
305
 
#---------------------------------------------------------------------------#
306
 
# Library/Console Test
307
 
#---------------------------------------------------------------------------#
308
 
# If this is called from console, we start main
309
 
#---------------------------------------------------------------------------#
310
 
if __name__ == "__main__":
311
 
    main()