05 April
2008

A reminder on how NOT to do software development.

Sometimes (or a lot of times) we developers lose the way to the green hills of the finished and polished products.

So, when I've found this quite old picture, I've printed, framed and placed it right atop of my development station. I look at it and read it every morning (now especially the box at the bottom-left) ;-)

The software development process.


Category Software Design 
Posted by alex at 00:18 | Comments (1) | Trackbacks (0)
14 March
2008

Some answers to the Devil Framework, the Universe and Everything.

Last week I've received an email with an interesting set of questions about the Devil Framework and I've learned that all email posted by our mail server is mysteriously considered spam by Yahoo!.

Let's start with a question from me: does any one know how to remove an IP address (X-YahooFilteredBulk: 66.139.77.213) from Yahoo's spam list?

Lets go on with the other question. (PS: I've not included the name of the person who sent the very interesting email for privacy reasons, but you know who you are. Thank you.)

First, the hard question. Is the release weeks, months, or years away?

Short answer: months (3 or 4, I hope).

Long answer: the product is finished and already operating at client plants, but it lacks enough documentation to make it usable for development by others except its creators (and sometimes we discover functionalities we have forgotten, shame on us). I'm working on it right now.

Now the easy questions. Are there any restrictions on the packages one can use in the plugins?

No, a plugin can install external libraries and extension of any. If you can build a wrapper to them, and you can legally use them, you can certainly add them to the framework using a plugin or a library module. Indeed almost all the system extensions (like SNMP, Ethernet/serial PLC interfaces, non-Qt widgets, etc.) are provided via plugins.

And no, the framework has no licensing restrictions against it.

What is required for resources to run the system? Memory? Hard Drive? Minimum CPU?

Off the shelf disk space usage is around ~100Mb for the server instances and ~120Mb for the client (size varies by platform distribution, with the Mac OS X one been the fattest of all with ~180Mb for the client).

For what concerns CPU and memory it really depends on what you are going to do with the framework.

More RAM you have the better: we have servers performing data mining jobs that require over 2Gb RAM for in memory data processing. Usually the server occupies a minimum of ~300Mb, the console a minimum of ~150-200Mb. Python is not very memory friendly and it tends to occupy large chunks of it before releasing any (anyhow it worths the trouble). Memory is cheep, buy as much as you can.

Any modern low-end single core CPU should be enough for basic data processing needs. As before, this depends on what you are going to do and how you are going to do it: Python is an interpreted language and for intensive data processing it requires more CPU cycles than, say, C: if you need top speed it's better to implement a Python wrapper around a C library. You can also take advantage of your shiny multi-core multi-processor system, despite the (in)famous Python GIL, using a simple module we have developed that lets you transparently run and query objects in separate Python processes.

Have you done any throughput tests? For the sake of discussion, if you had a system with 10 Collector nodes and 5 IceBridge Consoles, what is the order of magnitude for the number of events per second that could be handled.

When we made throughput tests for the Events sub-system we achieved ~3000-4000 events/sec per server node on old AMD Athlon 3200 CPUs. As always this depends on what you are doing on the collected events. Event pre-processing, post-processing and archival can drastically change performances (if you apply a Fourier transform on a set of events to generate another event that triggers some actions and is used for situations detection with other thousand of events, you can imagine that performances decrease a lot).

The IceBridge console has a different set of problems (and solutions).

The basic design concept around which the whole system is developed is that almost all event processing and manipulation has to be performed at the nodes nearest to the event source, with the upstream event flux containing only data meaningful to the "higher" layers.

An example may be useful: lets say we have a set of PLC that are queried by some Collectors managed by a central MCP server.

  • PLCs "generate" row data about the status of a device and control some of its operational aspects.

  • Collectors query PLCs and internal events with collected data are generated and processed locally. This Collectors are configured to not propagate this events to parent nodes. But, if some event "value" is above a configured alarm threshold an "alarm" event is generated and sent upstream. Moreover every 5 minutes an event containing "average" stats about the device is posted.

  • The MCP node receives alarm/stats events from child Collectors. Some kind of processing is performed on incoming events and at regular times the child Collectors/PLCs are queried about some kind of information. At scheduled times the PLCs are reconfigured to change operational behavior.

  • Plant operators are controlling plant machinery using IceBridge consoles connected to some specific Collector. They can control specific tools and receive alarms of interest. The plant CTO controls the general plant status from a console connected to the MCP. He can also control and override instructions provided by other operators to any connected machinery. The CEO is automatically alerted about production, storage and supply problems by a console connected to the MCP. The views all this people are using do not directly receive all the events generated by the Application Server infrastructure, but only the alarm events they are interest into. When they want to control some specific PLC a specific query is performed to the Tags System that manages the PLC itself.

The Tags System is a plugin that implements "abstracted tags". If you are familiar with SCADA software like WinCC and friends you already know what tags are. Otherwise, just consider a tag as a "smart" variable that gets (and sets) some data from a "device", processes it, archives it (using multiple consolidation functions, time resolutions and retention time spans) and generates "update" events the are propagated to selected parents.

The Tags System is fully integrated into the Devil Framework 's distributed architecture, so you can query any tag from any console or parent node with code like "env.api.tags_system.tags_managers['PLC_1'].tags['auto_destruction_status'].get ()" or using one of the provided widgets.

We have created tag providers for PLCs, custom refrigerator devices, SNMP, Devil Framework's internal system monitor, custom Python code, etc. Creating a new tag provider is quite easy and you can do it in a hundred lines of code.

Could each IceBridge console handle 2000-5000 events per second? If one created widgets that monitored and displayed u to 20 pieces of data per object read from an attached device and then created a view with 200 of these widgets on it, would it bring the system to it's knees or would that be reasonable?

I've not done any test like this but I think that updating so much data would slow down things quite a lot (to say the least). As I said above it's really better to use the distributed system to perform the processing and to selectively query/be notified for/of updates.

Your blog entries describe the IceBridge Console as a viewer for data being collected and processed from the other nodes like a SCADA system or a Distributed Control System Console. Could you foresee any problems running that in reverse?

We already have widgets that can be directly binded to tags, so in example, you can have a slider connected to a tag and when you move it the tag value is updated (and the device connected to it either), an vice-versa.

Is there any reason that someone could not set up widgets that would allow a user to create a graphical model in designer mode and then have the Application Server send data based on that model out to the attached devices?

If you want to know if you can use the Views Editor to directly write something like ladder logic programs, no, you can't. But there's no technical limitation that prevents you to write a widget that lets you develop programs in ladder logic.

Otherwise, if you want to design a form that lets you edit a "production recipe" (tables with some data, flags, etc.) and apply it to to the Application Server and to some of the connected devices, there's no problem at all. Remember that from any console, if you have the right permissions, you can transparently make any call to any node of the system.


Category Devil Framework 
Posted by alex at 13:38 | Comments (3) | Trackbacks (0)
06 March
2008

Atomic Batteries Included

Yes, we will deliver the Devil Framework with a full set of atomic batteries included...

Any Devil Framework software package (Application Server, IceBridge) lives in its own stand-alone environment that has almost no external dependencies (except for standard libraries). The list of libraries provided is quite impressive either excluding the ones added by the different plugins.

All packages include:

The IceBridge package includes:


Category Devil Framework 
Posted by alex at 11:36 | Comments (2) | Trackbacks (0)

HyperCard, Python, Views and a little sip of Pan Galactic Gargle Blaster (be careful...)

Been a big fan of HyperCard, it's not so strange I ended up implementing my own reinterpretation of it for the Devil Framework. Lets take a look....

1989 The Year We Make Contact

I remember that the first time I had the chance to lay my hands on a real Macintosh I felt in love...but not with the machine itself. Yes its user interface and design were mind blowing, MacWrite and MacPaint were so beautiful and...., well, there wasn't much more. Except HyperCard. That was a piece of software to love. It was so simple and so powerful and so innovative! I wanted it...but I had no Macintosh, just my beloved Amiga 500 (pictured below at the top of the Tower of Elders. One minute of respectful silence, please).

The Tower of Elders and Octane the Second

2002 Escape From Boredom

In a moment of boredom while toying with wxWindows (at that time, the GUI toolkit I was considering for our system) I came up with the idea of reimplementing HyperCard. The experiment went not so far; I got nothing more than a simple "live" GUI editor, but it was a start and a seed for better things to come.

2004 A Widget Odyssey

The need to implement an user interface design tool became a necessity almost at the beginning of the development of the Devil Framework: the system is designed around the idea of process control, and when you need to interface humans with a process, you need some graphical user interface. To get some inspiration and ideas I looked at the various HMI provided by SCADA systems (like WinCC, Wonderware, etc.), GUI builders (especially, and obviously, Qt Designer) and, last but not least, HyperCard and its offspring.

Lets take a look at the result.

A View That Looks At You

The good old HAL 9000 looks and speaks to you.

You can think at Views as HTML pages "on steroids" or HyperCard cards without the data storage. In a view you can:

  • Insert widgets. Widget are the base components of a view. By default you get almost any widgets defined in the Qt library (like buttons, combo boxes, etc.), plus a lot of not so standard ones (like MatPlot charts, OpenInventor 3D visualization, LEDs, video sources, etc.). New ones can be easily installed by plugins and made automatically and transparently available to all users (and indeed plugins like the AlarmsManager or the TagsManager expose a lot of them). A view is itself a special kind of widget that provides some additional functionalities (like timers, global actions and background "pipelines" and that can be embedded in (and not only) another view.

  • Manage layouts. Views provide a simple and powerful way of specifying the layout of child widgets (thanks to the Qt layout management system). Beside absolute placement, you can organize widgets into hierarchical groups that let you automatically organize widgets horizontally, vertically, in a two dimensional grid and within horizontal or vertical splitters. In the HAL 9000 view above, the eye and the text are placed in a vertical layout: changing the size of the window changes the size of the red eye to fill all available space.

  • Group widgets. After having assigned a set of widgets to a group you can consider them as a single macro widget. You can also place widgets into layers. A layer is a "lesser" group; widgets are not binded together but operations performed on a layer are propagated to all widgets in it.
  • Define "methods". If you consider a view to be like Python class, you can think at methods as class methods. You can define global methods (available to all views) and view/widget methods (available to all widgets inside the view or the widget). The only significant difference with standard Python class methods is that when a method is called it is searched not only in the calling widget, but also in all the parent ones up to the globals space. An example: lets say a global method named "test.echo" is defined and in a view there's a button widget with the "clicked" signal handler (see below) defined as "env.view.methods.test.echo ('Hello')". When the signal handler is invoked, the "test.echo" method is searched first in the button widget, than in the containing view (or any other parent widget) and finally in the global methods space.

  • Define "signal handlers". All views and widgets emit signals when the a user performs some action on them (like clicking the mouse button) or when some automatic event is triggered (like timer timeouts). You can bind code to be executed when one of this signals is emitted. A special method is the "init" one (called at view initialization time): every global defined here will be made available in all other signal handlers.

  • Actions: for almost any view and widget you can define popup menus with custom actions. Within a view you can also define menu and toolbar actions. In the HAL 9000 example a custom menu named "HAL" is defined together with a custom toolbar button: they give you the incredible chance to hear HAL speak!

Press CTRL-P to hear HAL.

Views are quite flexible in the way you can use and display them:

  • The most "natural" way is to use them in a Views Browser window.

  • You can also open a view from another as a dialog: you can pass an get data to an from the dialog.

  • Views can be embedded into another view. Child view's actions are blended with the parent ones.

  • You can insert a view inside any other GUI widget using the included ViewsPanel class. View actions will be added to the parent window (if not specified otherwise).

Live Editing!

Here you can see where HyperCard has really influenced me: a view can be edited whenever you like, live (if you have permissions to do so). Just click the "Toggle edit mode" button and the Views Browser transforms itself into the Views Editor.

Warning, preparing a Pan Galactic Gargle Blaster can bring the system to auto-destruction!

If you know the Qt Designer tool you'll feel right at home. You can add widgets by selecting the desired one from the widgets list and "paint" it into the view. Widget properties can be edited interactively from the properties panel. Double clicking on a widget opens up a configuration dialog from where you have access to more advanced functionalities. The common ones are:

  • Widget methods (see above).

  • Signal handlers (see above).

  • Event filters. Here we are talking about Devil Framework's events. Every view and widget receives and accepts events from connected Application Server instances. You can configure a widget (and by inheritance all its children) to accept only certain event types.

When you have made your changes, click the "Toggle edit mode" button and test the "updated" view right away. Simple, eh?

And whatever incredible and disruptive stunt you have performed on a view you can safely save it without warring about creating problems to other users: the modified version is visible to you only. When you'll feel comfortable to put it in production you'll just "commit" it. From then on the new version becomes the one used by everybody (you can always check out an older committed version, edit it, and make it the production one).

Dynamic Views

The Views Manager can also serve views generate on the fly. The following is a simple example of a dynamic view with two buttons in a layout:

# define the view generator code as a string >>> view_generator = '''
# The following code is executed on the server when a client requests the 
# associated view.
# Locals: 'view_name', 'view_uid', 'user_info', 'env' and 'sys'.
# Returns: a view definition

from DLevel.Devil.Common import Cons
from DLevel.Devil.Common.Plugin.ViewCompiler import ViewCompiler

vc = ViewCompiler ()
vc.set_name (view_name)

# add a layout to binded to the view's root
vc.add_widget ('/v_layout_1', 'layout', layout_type='vertical')
vc.set_root_layout ('/v_layout_1', True)

# add a button
vc.add_widget ('/v_layout_1/b1', 'push_button')
# set button text
vc.set_property ('/v_layout_1/b1', 'text', 'Welcome, %s.' % user_info[Cons.RPCServer.INFO_USER_NAME])
# add button to parent layout
vc.add_widget_to_layout ('/v_layout_1/b1')

# define another button and pass properties to be set
vc.add_widget ('/v_layout_1/b2', 'push_button', text='Button 2')
vc.add_widget_to_layout ('/v_layout_1/b2')

return vc.dump_widgets ()
'''

# create a new view
>>> uid = env.api.views.new ('/test/dynamic_view')
# assign the view generator string to the 'view_generator' metadata. That's it!
>>> env.api.views[uid].set_metadata ('view_generator', view_generator)

The dynamic view defined in the code above.

Obviously you can't edit a dynamic view within the Views Editor. More precisely, you can edit it and either save it, but when you'll re-open it, you'll get the dynamic view (except if there's an error in the view generator code; in this case you get the modified view you have saved).

For Your Eyes Only

As almost everything else in the Devil Framework, views are resources. This simple fact lets you finely tune access using the advanced features provided by the authorization sub-system. But this is a complex argument that will need a post on its own, may be from the author itself in its new, shiny, glamorous, super-beautiful and completely empty blog (but take a look at it nevertheless, as the cool logo and picture are made by me, the new Leonardo da Vinci :-D)

Another Minute Of Respectful Silence

The first picture in this post was about some really venerable and cool piece of hardware. I want to finish with a picture of a really venerable and cool piece of software. Bow and show respect ;-)

No comment needed.


Category Devil Framework 
Posted by alex at 00:44 | Comments (0) | Trackbacks (0)
25 February
2008

What happens when Python remembers me that programming is fun?

Looking back, last month was a really productive and fun one; I've designed and developed "from scratch" a fairly complete and extensible IDE and a quite powerful web application server as plugins for the Devil Framework. The two required really different sets of functionalities but where equally fast and fun to develop, thanks to the Python language itself, to the libraries available for it and also for the Devil Framework itself (that recursively uses Python,etc.).

Today I want to talk about the ScriptIDE plugin and how great are the two libraries that made it possible: PyQt and Qscintilla.

The plugin augments the IceBridge smart client with an integrated development environment designed (and shamelessly inspired by WingIDE and Eric) to enable developers to access all scripts (and more generally source code) made available by the different extensions available for the Devil Framework, like:

  • Actions

  • Event filters

  • Dynamic situations

  • Alarms

  • Database methods (SQL and Python)

  • Tags

  • Views

  • etc.

Two scripts in two different contexts: an SQL Method script in the Database Manager and a Signal Handler in a View.

Every extension provides its own ad-hoc user interface for managing its functionalities: this is something required, as scripts are just a single element of the whole configuration. As scripts are used everywhere, this also makes the script development experience less than excellent (at least).

So came the decision to develop the ScriptIDE plugin and with the following requirements:

  • Multiple script sources: every plugin can (and must) provide a set of script providers to give access to the script sources.

  • Tool plugins: plugins can add special tools to the IDE (like the “Search In Sources” tool), extending its functionality.

  • A minimum set of standard functionalities and tools:

  • Multiple editors.

  • Multiple views of the same document.

  • Syntax highlighting.

  • Auto-indent.

  • Simple auto-complete.

  • In editor search and replace.

  • Search and replace in provided sources.

  • Bookmarks.

  • Configurable user interface layout: plugins, tools and the user itself should be able to add/remove actions (and tools) at will.

  • Sessions management.

And in great astonishment and disbelief, I achieved all the above requirements in a couple of weeks! And in a mere 6000 lines of code!! And having some fun doing it!!! BAH!!!!

The editor part was e breeze as the QScintilla library/widget is really complete and simple to use and offers also some high-level functionalities (like the markers and the search and replace stuff). With a ~2000 LOC wrapper (really basic, no rocket science, just simple utilities) the editor come out fairly complete (no macros and scripts, by now).

GUI stuff such menus, toolbars and popup menus were easily managed thanks to the Devil Framework wrappers we have developed. You can add/remove/manage actions as in the code below:

# actions definition
actions = [
    {
        Action.NAME: 'close_current',
        Action.ACTION: self.on_close_current,
        Action.MENUBAR_PATH: '/g10/&File/fg10/15_close_current',
        Action.TOOLBAR_PATH: '/g10/file/fg10/15_close_current',
        Action.POPUP_PATH: '/g10/15_close_current',
        Action.LABEL: tr ('&Close', context='ScriptIDE'),
        Action.ACCEL: [Cons.GUI.MENU_ACCELERATOR_CTRL, 'd'], 
        Action.EXCLUSIVE: False,
        Action.TARGETS: Cons.GUI.TARGET_MENUBAR | Cons.GUI.TARGET_TOOLBAR | Cons.GUI.TARGET_POPUP_MENU,
        Action.ICON: 'images/file_close',
        Action.TEXT: tr ('Closes current editor.', context='ScriptIDE'),
        Action.WHATIS: tr ('', context='ScriptIDE'),
    },
]
# initialize actions displaying menubars and toolbars
self.init_actions (actions)
# get an action instance
a = self.get_action_by_name ('close_current')

# enable/disable and action
a.enabled = True
# show/hide an action
a.set_visible (True)
# set icon
a.set_icon ('images/devil')

# show a popup menu populated with given actions
self.show_popup ([a])

The same can be said about windows and dock windows: our wrappers automatically keep track of all the configurations, making state management mostly transparent. And as everything is data-driven,implementing pluggable tools ended up "implementing" a data structure like the one below:

DEFAULT_TOOL_WINDOWS = [
    {
        'name': 'right_toolbox',
        'label': 'Toolbox 1',
        'dock_config': { # dock window specific config
            'index': 0,
            'orientation': 'vertical',
            'position': 'right',
            'movable': True,
        },
        'show_hide_key': qt.Qt.Key_F10, # key sequence used to show or hide the toot window
        'sections': [
            {
                'style': 'tabs',
                'tabs_position': 'top',
                'tools': [
                    {
                        'name': 'script_browser',
                    },
                ],
            },
        ]
    },
]

Most of the common dialogs used in the IDE are "commodity" dialogs made available by the IceBridge console, so:

# to select a file for open (main_loop is the "historically misnamed" core singleton object)
self.main_loop.api.dialog.get_open_file_name (
    path = self.preferences.get ('insert_from_file_path', self.main_loop.platform.home_dir ()),
    filter = 'Python files (*.py);;Text files (*.txt);;All files (*.*)',
    caption = tr ('Select file to insert', context='ScriptIDE')
)

# to get an integer between 1 and last line in editor
i = self.main_loop.api.dialog.get_integer (
    caption = tr ('Goto Line', context='ScriptIDE'), 
    message = tr ('&Line Number:', context='ScriptIDE'),
    min = 1, 
    max = editor.lines (), 
    step = 1,
    data = 1,
)

Other common stuff was equally easy, like the management of hierarchical lists (QListView) so commonly used in modern GUIs:

# define the listview columns
columns = [
    ListView.Column ('name', tr ('Name', context='ScriptIDE'), width=80),
    ListView.Column ('source', tr ('Source', context='ScriptIDE'), stretchable=True),
    ListView.Column ('line', tr ('Line', context='ScriptIDE'), width=50),
]

self.bookmarks_list = ListView (
    self['bookmarks_list'], # Windows and Dialogs can find child widgets by name
    columns, 
    decorated_root = False
)

# populate the view
for b in bookmarks:
    name = b['name']
    label = b['label']
    line = b['line']

    # fill the row fields with proper values. Fields can be skipped if needed.
    f = {
        'name': ListView.Field ('name', name),
        'source': ListView.Field ('source', label),
        'line': ListView.Field ('line', str (line + 1)),
    }

    # define a row, with custom named attributes
    r = ListView.Row (
        None, self.bookmarks_list, f, 
        # custom named fields
        rtype = 'bookmark', 
        name = name,
        label = label,
        line = line,
        bookmark = b, 
        # a context menu action is associated to this row,
        # different actions can be specified for each row
        context_menu = self.on_context_menu 
    )

    if rows:
        rows.append (r)
    else:
        rows = r
			
# rows are updated: if an “equal” row is present, the old row is
# updated with the values of the new one, but the state is preserved:
# open, selected, etc.
self.bookmarks_list.update_rows (rows)

# state management; the state can be dumped and saved for later reuse
# state management is intelligent enough to skip obsolete data.
state = self.bookmarks_list.dump_state ()
self.bookmarks_list.load_state (state)

# rows can be directly manipulated
row.set_checked (True)
row.set_enabled (True, recursive=False)
row.select (True)
row.flash = True
# update 2 fields
row.field['name'].data = 'Foo'
row.field['source'].data = 'Bar'
row.update ()

And as you may have noted everything comes with internationalization included. And multi-platform!

I always wanted to develop my own IDE, but never gone too far has the task was too big. Not anymore! Python and PyQt (and our "little" framework) made it easy, fast and fun, removing all the tedious and infinitely prolonged stuff developers have to deal with. The IDE is not finished (next to be added: macros, scripts, templates and more providers) but it's fully working and it looks and behaves professionally.

Happy coding......


Posted by alex at 19:19 | Comments (0) | Trackbacks (0)
11 December
2007

DLNetSNMP added to Pypi

Just a note to say that I've added version 0.4 of DLNetSNMP to the Python Index.

If interested you can find more information and the dowloadable files at the new DLNetSNMP home page.

In this release a lot of serious bugs were fixed and a simple installation script added so you'll not need to perform manual changes in the code anymore.


Category Python 
Posted by root at 19:10 | Comments (0) | Trackbacks (0)
30 August
2007

A new version of the DLNetSNMP (Python ctypes wrapper for NetSNMP)

Just a small bug-fix update. Now it seems to work correctly, expecially the MIBs resolution stuff.

Be carefull in using it with threads, as the underling NetSNMP library is not thread-safe.

The code can be found at the DLNetSNMP home page.


Category Python 
Posted by alex at 10:24 | Comments (0) | Trackbacks (0)
15 August
2007

A small (but complete) Python ctypes-based Net-SNMP wrapper module.

Till last week we were using an heavy modified version of the old and unmaintained yapsnmp module to interface the Devil Framework with the cool Net-SNMP library. It was a real PITA to maintain it and to make it work on all the platforms we currently support (Linux, Windows and OS X). So I've begun a search for an alternative and (in examination order) found and discarded (for various reasons) the Python wrapper included into the Net-SNMP library itself, the pysnmp pure Python implementation and the ctypes-based pynetsnmp that comes with Zenoss.

I than decided to write my own version. Trying to avoid the "not invented here" syndrome I've started modifying the work already done by the cool people at Zenoss. It took only a full work day to (re)implement:

  • Synchronous and asynchronous "get", "getbulk", "walk" and "set" operations.

  • Complete MIBs management: set/get MIBs paths, load new MIBs, get OID descriptions from MIBs, oid to name (and vice versa) translation tools.

  • Session management, internal asynchronous events management, pluggable logger and meaningful error reporting.

I was quite impressed and satisfied till I've tested the code on Windows where I've found that:

  • some SNMP functions are missing in the "netsnmp.dll" (shutdown_mib, netsnmp_get_mib_directory, netsnmp_set_mib_directory, snprint_description at least)

  • the "netsnmp.dll" could not resolve any kind of network address (!!!!)

  • the asynchronous stuff could not work because the select function used to wait on SNMP sockets was receiving non-socket file descriptors (!!)

After 2 (two !!!) days of C code analysis and (useless) debugging (now I know quite well the network section of the Net-SNMP lib :-) ) I've learned that:

  • The WinSock2 library (used by Net-SNMP) needs to be initialized by hand calling WSAStartup or all networking stuff does not work (this is part my fault as I'd know from my Direct-X 1/2 programming days...but was a long time ago).

  • Under Linux (and OS X) the snmp_select_info function fills the provided fd_set list with all file descriptors opened for SNMP OR'd in, but under Windows the fd_set list has in the first item the number of file descriptors used, and than the file plain descriptors. Anyone can explain this?

Then was time to test the lib under OS X, but been Unix no problems should occur....WRONG! Under OS X ctypes (or the Net-SNMP lib) does not like to receive a statically nested scope function as a session callback. Anyone can explain this either? A hack was required.

Final notes before the code:

  • I've developed and tested the lib using Python 2.4.4, ctypes 1.0.1 and Net-SNMP 5.4.1.

  • I've not written the usual setup stuff, as you must change a couple of (simple) things before you can use the library outside of the Devil Framework source tree (read the comments at the beginning of the "DLNetSNMP.py" file).

  • You can find some simple tests and usage patters at the end of the "DLNetSNMP.py" file.

  • If you find bugs or have suggestions or comments or usage notes or anything related do not hesitate to contact me.

The code can be found at the DLNetSNMP home page.


Category Python 
Posted by alex at 22:58 | Comments (0) | Trackbacks (0)