Added concepts :
authoretisserant
Wed, 20 Aug 2008 00:11:40 +0200
changeset 203 cb9901076a21
parent 202 cd81a7a6e55c
child 204 f572ab819769
Added concepts :
- connector class (for PLC control, debug and flashing abstraction).
- builder class (C toolchain abstraction)

Added features :
- Pyro based connector.
- ctypes loader, load PLC shared object and run it
- ctypes based debugger embryo (not tested)
- gcc builder

Broken:
- Win32 runtime
- Most tests
Beremiz.py
Beremiz_service.py
Zeroconf.py
connectors/PYRO/__init__.py
connectors/USB/USB_connector.py
connectors/USB/__init__.py
connectors/__init__.py
discovery.py
images/Connect.png
images/Debug.png
images/Disconnect.png
images/Transfer.png
images/icons.svg
plugger.py
plugins/c_ext/c_ext.py
plugins/canfestival/canfestival.py
plugins/canfestival/cf_runtime.c
plugins/svgui/svgui.py
runtime/PLCObject.py
runtime/ServicePublisher.py
runtime/__init__.py
runtime/plc_Linux_main.c
runtime/plc_Win32_main.c
runtime/plc_common_main.c
runtime/plc_debug.c
targets/Linux/XSD
targets/Linux/__init__.py
targets/Rtai/XSD
targets/Rtai/__init__.py
targets/Rtai/target_rtai.py
targets/Win32/XSD
targets/Win32/__init__.py
targets/XSD_toolchain_gcc
targets/Xenomai/XSD
targets/Xenomai/__init__.py
targets/Xenomai/target_xenomai.py
targets/__init__.py
targets/toolchain_gcc.py
tests/linux/autom_ihm_rmll/CFileTest@c_ext/File1@C_File/baseplugin.xml
tests/linux/autom_ihm_rmll/CFileTest@c_ext/File1@C_File/cfile.xml
tests/linux/autom_ihm_rmll/CFileTest@c_ext/File1@C_File/plugin.xml
tests/linux/autom_ihm_rmll/CFileTest@c_ext/baseplugin.xml
tests/linux/autom_ihm_rmll/beremiz.xml
tests/linux/autom_ihm_rmll/canopen@canfestival/master@CanOpenNode/eds/Slave_2_0.eds
tests/linux/autom_ihm_rmll/canopen@canfestival/master@CanOpenNode/master.od
tests/linux/autom_ihm_rmll/canopen@canfestival/master@CanOpenNode/nodelist.cpj
tests/linux/autom_ihm_rmll/plc.xml
--- a/Beremiz.py	Tue Aug 12 16:27:07 2008 +0200
+++ b/Beremiz.py	Wed Aug 20 00:11:40 2008 +0200
@@ -357,13 +357,13 @@
         # Add beremiz's icon in top left corner of the frame
         self.SetIcon(wx.Icon(os.path.join(CWD, "images", "brz.ico"), wx.BITMAP_TYPE_ICO))
         
-        self.PluginRoot = PluginsRoot(self)
+        self.PluginRoot = PluginsRoot(self, self.Log)
         self.DisableEvents = False
         
         self.PluginInfos = {}
         
         if projectOpen:
-            self.PluginRoot.LoadProject(projectOpen, self.Log)
+            self.PluginRoot.LoadProject(projectOpen)
             self.RefreshPLCParams()
             self.RefreshPluginTree()
         
@@ -572,7 +572,7 @@
         else:
             msizer = wx.FlexGridSizer(cols=1)
         for plugin_method in plugin.PluginMethods:
-            if "method" in plugin_method:
+            if "method" in plugin_method and plugin_method.get("shown",True):
                 id = wx.NewId()
                 button = GenBitmapTextButton(id=id, parent=parent,
                     bitmap=wx.Bitmap(os.path.join(CWD, "%s.png"%plugin_method.get("bitmap", os.path.join("images", "Unknown")))), label=plugin_method["name"], 
@@ -675,7 +675,7 @@
         enablebutton.SetBitmapSelected(wx.Bitmap(os.path.join(CWD, 'images', 'Enabled.png')))
         enablebutton.SetToggle(plugin.MandatoryParams[1].getEnabled())
         def toggleenablebutton(event):
-            res = self.SetPluginParamsAttribute(plugin, "BaseParams.Enabled", enablebutton.GetToggle(), self.Log)
+            res = self.SetPluginParamsAttribute(plugin, "BaseParams.Enabled", enablebutton.GetToggle())
             enablebutton.SetToggle(res)
             event.Skip()
         enablebutton.Bind(wx.EVT_BUTTON, toggleenablebutton, id=enablebutton_id)
@@ -895,7 +895,7 @@
         
     def GetItemChannelChangedFunction(self, plugin, value):
         def OnPluginTreeItemChannelChanged(event):
-            res = self.SetPluginParamsAttribute(plugin, "BaseParams.IEC_Channel", value, self.Log)
+            res = self.SetPluginParamsAttribute(plugin, "BaseParams.IEC_Channel", value)
             event.Skip()
         return OnPluginTreeItemChannelChanged
     
@@ -923,7 +923,7 @@
             # Disable button to prevent re-entrant call 
             event.GetEventObject().Disable()
             # Call
-            getattr(plugin,method)(self.Log)
+            getattr(plugin,method)()
             # Re-enable button 
             event.GetEventObject().Enable()
             # Trigger refresh on Idle
@@ -933,14 +933,14 @@
     
     def GetChoiceCallBackFunction(self, choicectrl, plugin, path):
         def OnChoiceChanged(event):
-            res = self.SetPluginParamsAttribute(plugin, path, choicectrl.GetStringSelection(), self.Log)
+            res = self.SetPluginParamsAttribute(plugin, path, choicectrl.GetStringSelection())
             choicectrl.SetStringSelection(res)
             event.Skip()
         return OnChoiceChanged
     
     def GetChoiceContentCallBackFunction(self, choicectrl, staticboxsizer, plugin, path):
         def OnChoiceContentChanged(event):
-            res = self.SetPluginParamsAttribute(plugin, path, choicectrl.GetStringSelection(), self.Log)
+            res = self.SetPluginParamsAttribute(plugin, path, choicectrl.GetStringSelection())
             if wx.VERSION < (2, 8, 0):
                 self.ParamsPanel.Freeze()
                 choicectrl.SetStringSelection(res)
@@ -958,14 +958,14 @@
     
     def GetTextCtrlCallBackFunction(self, textctrl, plugin, path):
         def OnTextCtrlChanged(event):
-            res = self.SetPluginParamsAttribute(plugin, path, textctrl.GetValue(), self.Log)
+            res = self.SetPluginParamsAttribute(plugin, path, textctrl.GetValue())
             textctrl.SetValue(res)
             event.Skip()
         return OnTextCtrlChanged
     
     def GetCheckBoxCallBackFunction(self, chkbx, plugin, path):
         def OnCheckBoxChanged(event):
-            res = self.SetPluginParamsAttribute(plugin, path, chkbx.IsChecked(), self.Log)
+            res = self.SetPluginParamsAttribute(plugin, path, chkbx.IsChecked())
             chkbx.SetValue(res)
             event.Skip()
         return OnCheckBoxChanged
@@ -1112,7 +1112,7 @@
         if dialog.ShowModal() == wx.ID_OK:
             projectpath = dialog.GetPath()
             if os.path.isdir(projectpath):
-                result = self.PluginRoot.LoadProject(projectpath, self.Log)
+                result = self.PluginRoot.LoadProject(projectpath)
                 if not result:
                     self.RefreshPLCParams()
                     self.RefreshPluginTree()
@@ -1216,7 +1216,7 @@
         dialog = wx.TextEntryDialog(self, "Please enter a name for plugin:", "Add Plugin", "", wx.OK|wx.CANCEL)
         if dialog.ShowModal() == wx.ID_OK:
             PluginName = dialog.GetValue()
-            plugin.PlugAddChild(PluginName, PluginType, self.Log)
+            plugin.PlugAddChild(PluginName, PluginType)
             self.RefreshPluginTree()
         dialog.Destroy()
     
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Beremiz_service.py	Wed Aug 20 00:11:40 2008 +0200
@@ -0,0 +1,98 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+#This file is part of Beremiz, a Integrated Development Environment for
+#programming IEC 61131-3 automates supporting plcopen standard and CanFestival. 
+#
+#Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
+#
+#See COPYING file for copyrights details.
+#
+#This library is free software; you can redistribute it and/or
+#modify it under the terms of the GNU General Public
+#License as published by the Free Software Foundation; either
+#version 2.1 of the License, or (at your option) any later version.
+#
+#This library is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#General Public License for more details.
+#
+#You should have received a copy of the GNU General Public
+#License along with this library; if not, write to the Free Software
+#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+import os, sys, getopt, socket
+
+def usage():
+    print "\nUsage of Beremiz PLC execution service :"
+    print "\n   %s [PLC path]\n"%sys.argv[0]
+
+try:
+    opts, args = getopt.getopt(sys.argv[1:], "h", ["help"])
+except getopt.GetoptError:
+    # print help information and exit:
+    usage()
+    sys.exit(2)
+
+for o, a in opts:
+    if o in ("-h", "--help"):
+        usage()
+        sys.exit()
+
+if len(args) > 1:
+    usage()
+    sys.exit()
+elif len(args) == 1:
+    WorkingDir = args[0]
+elif len(args) == 0:
+    WorkingDir = os.getcwd()
+else:
+    usage()
+    sys.exit()
+
+from runtime import PLCObject, ServicePublisher
+import Pyro.core as pyro
+
+if not os.path.isdir(WorkingDir):
+    os.mkdir(WorkingDir)
+
+# type: fully qualified service type name
+type = '_PYRO._tcp.local.'
+# name: fully qualified service name
+name = 'First test.%s'%(type)
+# address: IP address as unsigned short, network byte order
+
+def gethostaddr(dst = '224.0.1.41'):
+    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+    try:
+        s.connect((dst, 7))
+        (host, port) = s.getsockname()
+        s.close()
+        if host != '0.0.0.0':
+            return host
+    except error:
+        pass
+    return socket.gethostbyname(socket.gethostname())
+
+ip = gethostaddr()
+# port: port that the service runs on
+port = 3000
+# properties: dictionary of properties (or a string holding the bytes for the text field)
+serviceproperties = {'description':'Remote control for PLC'}
+
+pyro.initServer()
+daemon=pyro.Daemon(host=ip, port=port)
+uri = daemon.connect(PLCObject(WorkingDir, daemon),"PLCObject")
+print "The daemon runs on port :",daemon.port
+print "The object's uri is :",uri
+print "The working directory :",WorkingDir
+print "Publish service on local network"
+
+ip_32b = socket.inet_aton(ip)
+# Configure and publish service
+service = ServicePublisher.PublishService()
+service.ConfigureService(type, name, ip_32b, port, serviceproperties)
+service.PublishService()
+
+daemon.requestLoop()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Zeroconf.py	Wed Aug 20 00:11:40 2008 +0200
@@ -0,0 +1,1556 @@
+""" Multicast DNS Service Discovery for Python, v0.12
+    Copyright (C) 2003, Paul Scott-Murphy
+
+    This module provides a framework for the use of DNS Service Discovery
+    using IP multicast.  It has been tested against the JRendezvous
+    implementation from <a href="http://strangeberry.com">StrangeBerry</a>,
+    and against the mDNSResponder from Mac OS X 10.3.8.
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+	
+"""
+
+"""0.12 update - allow selection of binding interface
+		 typo fix - Thanks A. M. Kuchlingi
+		 removed all use of word 'Rendezvous' - this is an API change"""
+
+"""0.11 update - correction to comments for addListener method
+                 support for new record types seen from OS X
+				  - IPv6 address
+				  - hostinfo
+				 ignore unknown DNS record types
+				 fixes to name decoding
+				 works alongside other processes using port 5353 (e.g. on Mac OS X)
+				 tested against Mac OS X 10.3.2's mDNSResponder
+				 corrections to removal of list entries for service browser"""
+
+"""0.10 update - Jonathon Paisley contributed these corrections:
+                 always multicast replies, even when query is unicast
+				 correct a pointer encoding problem
+				 can now write records in any order
+				 traceback shown on failure
+				 better TXT record parsing
+				 server is now separate from name
+				 can cancel a service browser
+
+				 modified some unit tests to accommodate these changes"""
+
+"""0.09 update - remove all records on service unregistration
+                 fix DOS security problem with readName"""
+
+"""0.08 update - changed licensing to LGPL"""
+
+"""0.07 update - faster shutdown on engine
+                 pointer encoding of outgoing names
+				 ServiceBrowser now works
+				 new unit tests"""
+
+"""0.06 update - small improvements with unit tests
+                 added defined exception types
+				 new style objects
+				 fixed hostname/interface problem
+				 fixed socket timeout problem
+				 fixed addServiceListener() typo bug
+				 using select() for socket reads
+				 tested on Debian unstable with Python 2.2.2"""
+
+"""0.05 update - ensure case insensitivty on domain names
+                 support for unicast DNS queries"""
+
+"""0.04 update - added some unit tests
+                 added __ne__ adjuncts where required
+				 ensure names end in '.local.'
+				 timeout on receiving socket for clean shutdown"""
+
+__author__ = "Paul Scott-Murphy"
+__email__ = "paul at scott dash murphy dot com"
+__version__ = "0.12"
+
+import string
+import time
+import struct
+import socket
+import threading
+import select
+import traceback
+
+__all__ = ["Zeroconf", "ServiceInfo", "ServiceBrowser"]
+
+# hook for threads
+
+globals()['_GLOBAL_DONE'] = 0
+
+# Some timing constants
+
+_UNREGISTER_TIME = 125
+_CHECK_TIME = 175
+_REGISTER_TIME = 225
+_LISTENER_TIME = 200
+_BROWSER_TIME = 500
+
+# Some DNS constants
+	
+_MDNS_ADDR = '224.0.0.251'
+_MDNS_PORT = 5353;
+_DNS_PORT = 53;
+_DNS_TTL = 60 * 60; # one hour default TTL
+
+_MAX_MSG_TYPICAL = 1460 # unused
+_MAX_MSG_ABSOLUTE = 8972
+
+_FLAGS_QR_MASK = 0x8000 # query response mask
+_FLAGS_QR_QUERY = 0x0000 # query
+_FLAGS_QR_RESPONSE = 0x8000 # response
+
+_FLAGS_AA = 0x0400 # Authorative answer
+_FLAGS_TC = 0x0200 # Truncated
+_FLAGS_RD = 0x0100 # Recursion desired
+_FLAGS_RA = 0x8000 # Recursion available
+
+_FLAGS_Z = 0x0040 # Zero
+_FLAGS_AD = 0x0020 # Authentic data
+_FLAGS_CD = 0x0010 # Checking disabled
+
+_CLASS_IN = 1
+_CLASS_CS = 2
+_CLASS_CH = 3
+_CLASS_HS = 4
+_CLASS_NONE = 254
+_CLASS_ANY = 255
+_CLASS_MASK = 0x7FFF
+_CLASS_UNIQUE = 0x8000
+
+_TYPE_A = 1
+_TYPE_NS = 2
+_TYPE_MD = 3
+_TYPE_MF = 4
+_TYPE_CNAME = 5
+_TYPE_SOA = 6
+_TYPE_MB = 7
+_TYPE_MG = 8
+_TYPE_MR = 9
+_TYPE_NULL = 10
+_TYPE_WKS = 11
+_TYPE_PTR = 12
+_TYPE_HINFO = 13
+_TYPE_MINFO = 14
+_TYPE_MX = 15
+_TYPE_TXT = 16
+_TYPE_AAAA = 28
+_TYPE_SRV = 33
+_TYPE_ANY =  255
+
+# Mapping constants to names
+
+_CLASSES = { _CLASS_IN : "in",
+			 _CLASS_CS : "cs",
+			 _CLASS_CH : "ch",
+			 _CLASS_HS : "hs",
+			 _CLASS_NONE : "none",
+			 _CLASS_ANY : "any" }
+
+_TYPES = { _TYPE_A : "a",
+		   _TYPE_NS : "ns",
+		   _TYPE_MD : "md",
+		   _TYPE_MF : "mf",
+		   _TYPE_CNAME : "cname",
+		   _TYPE_SOA : "soa",
+		   _TYPE_MB : "mb",
+		   _TYPE_MG : "mg",
+		   _TYPE_MR : "mr",
+		   _TYPE_NULL : "null",
+		   _TYPE_WKS : "wks",
+		   _TYPE_PTR : "ptr",
+		   _TYPE_HINFO : "hinfo",
+		   _TYPE_MINFO : "minfo",
+		   _TYPE_MX : "mx",
+		   _TYPE_TXT : "txt",
+		   _TYPE_AAAA : "quada",
+		   _TYPE_SRV : "srv",
+		   _TYPE_ANY : "any" }
+
+# utility functions
+
+def currentTimeMillis():
+	"""Current system time in milliseconds"""
+	return time.time() * 1000
+
+# Exceptions
+
+class NonLocalNameException(Exception):
+	pass
+
+class NonUniqueNameException(Exception):
+	pass
+
+class NamePartTooLongException(Exception):
+	pass
+
+class AbstractMethodException(Exception):
+	pass
+
+class BadTypeInNameException(Exception):
+	pass
+
+# implementation classes
+
+class DNSEntry(object):
+	"""A DNS entry"""
+	
+	def __init__(self, name, type, clazz):
+		self.key = string.lower(name)
+		self.name = name
+		self.type = type
+		self.clazz = clazz & _CLASS_MASK
+		self.unique = (clazz & _CLASS_UNIQUE) != 0
+
+	def __eq__(self, other):
+		"""Equality test on name, type, and class"""
+		if isinstance(other, DNSEntry):
+			return self.name == other.name and self.type == other.type and self.clazz == other.clazz
+		return 0
+
+	def __ne__(self, other):
+		"""Non-equality test"""
+		return not self.__eq__(other)
+
+	def getClazz(self, clazz):
+		"""Class accessor"""
+		try:
+			return _CLASSES[clazz]
+		except:
+			return "?(%s)" % (clazz)
+
+	def getType(self, type):
+		"""Type accessor"""
+		try:
+			return _TYPES[type]
+		except:
+			return "?(%s)" % (type)
+
+	def toString(self, hdr, other):
+		"""String representation with additional information"""
+		result = "%s[%s,%s" % (hdr, self.getType(self.type), self.getClazz(self.clazz))
+		if self.unique:
+			result += "-unique,"
+		else:
+			result += ","
+		result += self.name
+		if other is not None:
+			result += ",%s]" % (other)
+		else:
+			result += "]"
+		return result
+
+class DNSQuestion(DNSEntry):
+	"""A DNS question entry"""
+	
+	def __init__(self, name, type, clazz):
+		if not name.endswith(".local."):
+			raise NonLocalNameException
+		DNSEntry.__init__(self, name, type, clazz)
+
+	def answeredBy(self, rec):
+		"""Returns true if the question is answered by the record"""
+		return self.clazz == rec.clazz and (self.type == rec.type or self.type == _TYPE_ANY) and self.name == rec.name
+
+	def __repr__(self):
+		"""String representation"""
+		return DNSEntry.toString(self, "question", None)
+
+
+class DNSRecord(DNSEntry):
+	"""A DNS record - like a DNS entry, but has a TTL"""
+	
+	def __init__(self, name, type, clazz, ttl):
+		DNSEntry.__init__(self, name, type, clazz)
+		self.ttl = ttl
+		self.created = currentTimeMillis()
+
+	def __eq__(self, other):
+		"""Tests equality as per DNSRecord"""
+		if isinstance(other, DNSRecord):
+			return DNSEntry.__eq__(self, other)
+		return 0
+
+	def suppressedBy(self, msg):
+		"""Returns true if any answer in a message can suffice for the
+		information held in this record."""
+		for record in msg.answers:
+			if self.suppressedByAnswer(record):
+				return 1
+		return 0
+
+	def suppressedByAnswer(self, other):
+		"""Returns true if another record has same name, type and class,
+		and if its TTL is at least half of this record's."""
+		if self == other and other.ttl > (self.ttl / 2):
+			return 1
+		return 0
+
+	def getExpirationTime(self, percent):
+		"""Returns the time at which this record will have expired
+		by a certain percentage."""
+		return self.created + (percent * self.ttl * 10)
+
+	def getRemainingTTL(self, now):
+		"""Returns the remaining TTL in seconds."""
+		return max(0, (self.getExpirationTime(100) - now) / 1000)
+
+	def isExpired(self, now):
+		"""Returns true if this record has expired."""
+		return self.getExpirationTime(100) <= now
+
+	def isStale(self, now):
+		"""Returns true if this record is at least half way expired."""
+		return self.getExpirationTime(50) <= now
+
+	def resetTTL(self, other):
+		"""Sets this record's TTL and created time to that of
+		another record."""
+		self.created = other.created
+		self.ttl = other.ttl
+
+	def write(self, out):
+		"""Abstract method"""
+		raise AbstractMethodException
+
+	def toString(self, other):
+		"""String representation with addtional information"""
+		arg = "%s/%s,%s" % (self.ttl, self.getRemainingTTL(currentTimeMillis()), other)
+		return DNSEntry.toString(self, "record", arg)
+
+class DNSAddress(DNSRecord):
+	"""A DNS address record"""
+	
+	def __init__(self, name, type, clazz, ttl, address):
+		DNSRecord.__init__(self, name, type, clazz, ttl)
+		self.address = address
+
+	def write(self, out):
+		"""Used in constructing an outgoing packet"""
+		out.writeString(self.address, len(self.address))
+
+	def __eq__(self, other):
+		"""Tests equality on address"""
+		if isinstance(other, DNSAddress):
+			return self.address == other.address
+		return 0
+
+	def __repr__(self):
+		"""String representation"""
+		try:
+			return socket.inet_ntoa(self.address)
+		except:
+			return self.address
+
+class DNSHinfo(DNSRecord):
+	"""A DNS host information record"""
+
+	def __init__(self, name, type, clazz, ttl, cpu, os):
+		DNSRecord.__init__(self, name, type, clazz, ttl)
+		self.cpu = cpu
+		self.os = os
+
+	def write(self, out):
+		"""Used in constructing an outgoing packet"""
+		out.writeString(self.cpu, len(self.cpu))
+		out.writeString(self.os, len(self.os))
+
+	def __eq__(self, other):
+		"""Tests equality on cpu and os"""
+		if isinstance(other, DNSHinfo):
+			return self.cpu == other.cpu and self.os == other.os
+		return 0
+
+	def __repr__(self):
+		"""String representation"""
+		return self.cpu + " " + self.os
+	
+class DNSPointer(DNSRecord):
+	"""A DNS pointer record"""
+	
+	def __init__(self, name, type, clazz, ttl, alias):
+		DNSRecord.__init__(self, name, type, clazz, ttl)
+		self.alias = alias
+
+	def write(self, out):
+		"""Used in constructing an outgoing packet"""
+		out.writeName(self.alias)
+
+	def __eq__(self, other):
+		"""Tests equality on alias"""
+		if isinstance(other, DNSPointer):
+			return self.alias == other.alias
+		return 0
+
+	def __repr__(self):
+		"""String representation"""
+		return self.toString(self.alias)
+
+class DNSText(DNSRecord):
+	"""A DNS text record"""
+	
+	def __init__(self, name, type, clazz, ttl, text):
+		DNSRecord.__init__(self, name, type, clazz, ttl)
+		self.text = text
+
+	def write(self, out):
+		"""Used in constructing an outgoing packet"""
+		out.writeString(self.text, len(self.text))
+
+	def __eq__(self, other):
+		"""Tests equality on text"""
+		if isinstance(other, DNSText):
+			return self.text == other.text
+		return 0
+
+	def __repr__(self):
+		"""String representation"""
+		if len(self.text) > 10:
+			return self.toString(self.text[:7] + "...")
+		else:
+			return self.toString(self.text)
+
+class DNSService(DNSRecord):
+	"""A DNS service record"""
+	
+	def __init__(self, name, type, clazz, ttl, priority, weight, port, server):
+		DNSRecord.__init__(self, name, type, clazz, ttl)
+		self.priority = priority
+		self.weight = weight
+		self.port = port
+		self.server = server
+
+	def write(self, out):
+		"""Used in constructing an outgoing packet"""
+		out.writeShort(self.priority)
+		out.writeShort(self.weight)
+		out.writeShort(self.port)
+		out.writeName(self.server)
+
+	def __eq__(self, other):
+		"""Tests equality on priority, weight, port and server"""
+		if isinstance(other, DNSService):
+			return self.priority == other.priority and self.weight == other.weight and self.port == other.port and self.server == other.server
+		return 0
+
+	def __repr__(self):
+		"""String representation"""
+		return self.toString("%s:%s" % (self.server, self.port))
+
+class DNSIncoming(object):
+	"""Object representation of an incoming DNS packet"""
+	
+	def __init__(self, data):
+		"""Constructor from string holding bytes of packet"""
+		self.offset = 0
+		self.data = data
+		self.questions = []
+		self.answers = []
+		self.numQuestions = 0
+		self.numAnswers = 0
+		self.numAuthorities = 0
+		self.numAdditionals = 0
+		
+		self.readHeader()
+		self.readQuestions()
+		self.readOthers()
+
+	def readHeader(self):
+		"""Reads header portion of packet"""
+		format = '!HHHHHH'
+		length = struct.calcsize(format)
+		info = struct.unpack(format, self.data[self.offset:self.offset+length])
+		self.offset += length
+
+		self.id = info[0]
+		self.flags = info[1]
+		self.numQuestions = info[2]
+		self.numAnswers = info[3]
+		self.numAuthorities = info[4]
+		self.numAdditionals = info[5]
+
+	def readQuestions(self):
+		"""Reads questions section of packet"""
+		format = '!HH'
+		length = struct.calcsize(format)
+		for i in range(0, self.numQuestions):
+			name = self.readName()
+			info = struct.unpack(format, self.data[self.offset:self.offset+length])
+			self.offset += length
+			
+			question = DNSQuestion(name, info[0], info[1])
+			self.questions.append(question)
+
+	def readInt(self):
+		"""Reads an integer from the packet"""
+		format = '!I'
+		length = struct.calcsize(format)
+		info = struct.unpack(format, self.data[self.offset:self.offset+length])
+		self.offset += length
+		return info[0]
+
+	def readCharacterString(self):
+		"""Reads a character string from the packet"""
+		length = ord(self.data[self.offset])
+		self.offset += 1
+		return self.readString(length)
+
+	def readString(self, len):
+		"""Reads a string of a given length from the packet"""
+		format = '!' + str(len) + 's'
+		length =  struct.calcsize(format)
+		info = struct.unpack(format, self.data[self.offset:self.offset+length])
+		self.offset += length
+		return info[0]
+
+	def readUnsignedShort(self):
+		"""Reads an unsigned short from the packet"""
+		format = '!H'
+		length = struct.calcsize(format)
+		info = struct.unpack(format, self.data[self.offset:self.offset+length])
+		self.offset += length
+		return info[0]
+
+	def readOthers(self):
+		"""Reads the answers, authorities and additionals section of the packet"""
+		format = '!HHiH'
+		length = struct.calcsize(format)
+		n = self.numAnswers + self.numAuthorities + self.numAdditionals
+		for i in range(0, n):
+			domain = self.readName()
+			info = struct.unpack(format, self.data[self.offset:self.offset+length])
+			self.offset += length
+
+			rec = None
+			if info[0] == _TYPE_A:
+				rec = DNSAddress(domain, info[0], info[1], info[2], self.readString(4))
+			elif info[0] == _TYPE_CNAME or info[0] == _TYPE_PTR:
+				rec = DNSPointer(domain, info[0], info[1], info[2], self.readName())
+			elif info[0] == _TYPE_TXT:
+				rec = DNSText(domain, info[0], info[1], info[2], self.readString(info[3]))
+			elif info[0] == _TYPE_SRV:
+				rec = DNSService(domain, info[0], info[1], info[2], self.readUnsignedShort(), self.readUnsignedShort(), self.readUnsignedShort(), self.readName())
+			elif info[0] == _TYPE_HINFO:
+				rec = DNSHinfo(domain, info[0], info[1], info[2], self.readCharacterString(), self.readCharacterString())
+			elif info[0] == _TYPE_AAAA:
+				rec = DNSAddress(domain, info[0], info[1], info[2], self.readString(16))
+			else:
+				# Try to ignore types we don't know about
+				# this may mean the rest of the name is
+				# unable to be parsed, and may show errors
+				# so this is left for debugging.  New types
+				# encountered need to be parsed properly.
+				#
+				#print "UNKNOWN TYPE = " + str(info[0])
+				#raise BadTypeInNameException
+				pass
+
+			if rec is not None:
+				self.answers.append(rec)
+				
+	def isQuery(self):
+		"""Returns true if this is a query"""
+		return (self.flags & _FLAGS_QR_MASK) == _FLAGS_QR_QUERY
+
+	def isResponse(self):
+		"""Returns true if this is a response"""
+		return (self.flags & _FLAGS_QR_MASK) == _FLAGS_QR_RESPONSE
+
+	def readUTF(self, offset, len):
+		"""Reads a UTF-8 string of a given length from the packet"""
+		result = self.data[offset:offset+len].decode('utf-8')
+		return result
+		
+	def readName(self):
+		"""Reads a domain name from the packet"""
+		result = ''
+		off = self.offset
+		next = -1
+		first = off
+
+		while 1:
+			len = ord(self.data[off])
+			off += 1
+			if len == 0:
+				break
+			t = len & 0xC0
+			if t == 0x00:
+				result = ''.join((result, self.readUTF(off, len) + '.'))
+				off += len
+			elif t == 0xC0:
+				if next < 0:
+					next = off + 1
+				off = ((len & 0x3F) << 8) | ord(self.data[off])
+				if off >= first:
+					raise "Bad domain name (circular) at " + str(off)
+				first = off
+			else:
+				raise "Bad domain name at " + str(off)
+
+		if next >= 0:
+			self.offset = next
+		else:
+			self.offset = off
+
+		return result
+	
+		
+class DNSOutgoing(object):
+	"""Object representation of an outgoing packet"""
+	
+	def __init__(self, flags, multicast = 1):
+		self.finished = 0
+		self.id = 0
+		self.multicast = multicast
+		self.flags = flags
+		self.names = {}
+		self.data = []
+		self.size = 12
+		
+		self.questions = []
+		self.answers = []
+		self.authorities = []
+		self.additionals = []
+
+	def addQuestion(self, record):
+		"""Adds a question"""
+		self.questions.append(record)
+
+	def addAnswer(self, inp, record):
+		"""Adds an answer"""
+		if not record.suppressedBy(inp):
+			self.addAnswerAtTime(record, 0)
+
+	def addAnswerAtTime(self, record, now):
+		"""Adds an answer if if does not expire by a certain time"""
+		if record is not None:
+			if now == 0 or not record.isExpired(now):
+				self.answers.append((record, now))
+
+	def addAuthorativeAnswer(self, record):
+		"""Adds an authoritative answer"""
+		self.authorities.append(record)
+
+	def addAdditionalAnswer(self, record):
+		"""Adds an additional answer"""
+		self.additionals.append(record)
+
+	def writeByte(self, value):
+		"""Writes a single byte to the packet"""
+		format = '!c'
+		self.data.append(struct.pack(format, chr(value)))
+		self.size += 1
+
+	def insertShort(self, index, value):
+		"""Inserts an unsigned short in a certain position in the packet"""
+		format = '!H'
+		self.data.insert(index, struct.pack(format, value))
+		self.size += 2
+		
+	def writeShort(self, value):
+		"""Writes an unsigned short to the packet"""
+		format = '!H'
+		self.data.append(struct.pack(format, value))
+		self.size += 2
+
+	def writeInt(self, value):
+		"""Writes an unsigned integer to the packet"""
+		format = '!I'
+		self.data.append(struct.pack(format, int(value)))
+		self.size += 4
+
+	def writeString(self, value, length):
+		"""Writes a string to the packet"""
+		format = '!' + str(length) + 's'
+		self.data.append(struct.pack(format, value))
+		self.size += length
+
+	def writeUTF(self, s):
+		"""Writes a UTF-8 string of a given length to the packet"""
+		utfstr = s.encode('utf-8')
+		length = len(utfstr)
+		if length > 64:
+			raise NamePartTooLongException
+		self.writeByte(length)
+		self.writeString(utfstr, length)
+
+	def writeName(self, name):
+		"""Writes a domain name to the packet"""
+
+		try:
+			# Find existing instance of this name in packet
+			#
+			index = self.names[name]
+		except KeyError:
+			# No record of this name already, so write it
+			# out as normal, recording the location of the name
+			# for future pointers to it.
+			#
+			self.names[name] = self.size
+			parts = name.split('.')
+			if parts[-1] == '':
+				parts = parts[:-1]
+			for part in parts:
+				self.writeUTF(part)
+			self.writeByte(0)
+			return
+
+		# An index was found, so write a pointer to it
+		#
+		self.writeByte((index >> 8) | 0xC0)
+		self.writeByte(index)
+
+	def writeQuestion(self, question):
+		"""Writes a question to the packet"""
+		self.writeName(question.name)
+		self.writeShort(question.type)
+		self.writeShort(question.clazz)
+
+	def writeRecord(self, record, now):
+		"""Writes a record (answer, authoritative answer, additional) to
+		the packet"""
+		self.writeName(record.name)
+		self.writeShort(record.type)
+		if record.unique and self.multicast:
+			self.writeShort(record.clazz | _CLASS_UNIQUE)
+		else:
+			self.writeShort(record.clazz)
+		if now == 0:
+			self.writeInt(record.ttl)
+		else:
+			self.writeInt(record.getRemainingTTL(now))
+		index = len(self.data)
+		# Adjust size for the short we will write before this record
+		#
+		self.size += 2
+		record.write(self)
+		self.size -= 2
+		
+		length = len(''.join(self.data[index:]))
+		self.insertShort(index, length) # Here is the short we adjusted for
+
+	def packet(self):
+		"""Returns a string containing the packet's bytes
+
+		No further parts should be added to the packet once this
+		is done."""
+		if not self.finished:
+			self.finished = 1
+			for question in self.questions:
+				self.writeQuestion(question)
+			for answer, time in self.answers:
+				self.writeRecord(answer, time)
+			for authority in self.authorities:
+				self.writeRecord(authority, 0)
+			for additional in self.additionals:
+				self.writeRecord(additional, 0)
+		
+			self.insertShort(0, len(self.additionals))
+			self.insertShort(0, len(self.authorities))
+			self.insertShort(0, len(self.answers))
+			self.insertShort(0, len(self.questions))
+			self.insertShort(0, self.flags)
+			if self.multicast:
+				self.insertShort(0, 0)
+			else:
+				self.insertShort(0, self.id)
+		return ''.join(self.data)
+
+
+class DNSCache(object):
+	"""A cache of DNS entries"""
+	
+	def __init__(self):
+		self.cache = {}
+
+	def add(self, entry):
+		"""Adds an entry"""
+		try:
+			list = self.cache[entry.key]
+		except:
+			list = self.cache[entry.key] = []
+		list.append(entry)
+
+	def remove(self, entry):
+		"""Removes an entry"""
+		try:
+			list = self.cache[entry.key]
+			list.remove(entry)
+		except:
+			pass
+
+	def get(self, entry):
+		"""Gets an entry by key.  Will return None if there is no
+		matching entry."""
+		try:
+			list = self.cache[entry.key]
+			return list[list.index(entry)]
+		except:
+			return None
+
+	def getByDetails(self, name, type, clazz):
+		"""Gets an entry by details.  Will return None if there is
+		no matching entry."""
+		entry = DNSEntry(name, type, clazz)
+		return self.get(entry)
+
+	def entriesWithName(self, name):
+		"""Returns a list of entries whose key matches the name."""
+		try:
+			return self.cache[name]
+		except:
+			return []
+
+	def entries(self):
+		"""Returns a list of all entries"""
+		def add(x, y): return x+y
+		try:
+			return reduce(add, self.cache.values())
+		except:
+			return []
+
+
+class Engine(threading.Thread):
+	"""An engine wraps read access to sockets, allowing objects that
+	need to receive data from sockets to be called back when the
+	sockets are ready.
+
+	A reader needs a handle_read() method, which is called when the socket
+	it is interested in is ready for reading.
+
+	Writers are not implemented here, because we only send short
+	packets.
+	"""
+
+	def __init__(self, zeroconf):
+		threading.Thread.__init__(self)
+		self.zeroconf = zeroconf
+		self.readers = {} # maps socket to reader
+		self.timeout = 5
+		self.condition = threading.Condition()
+		self.start()
+
+	def run(self):
+		while not globals()['_GLOBAL_DONE']:
+			rs = self.getReaders()
+			if len(rs) == 0:
+				# No sockets to manage, but we wait for the timeout
+				# or addition of a socket
+				#
+				self.condition.acquire()
+				self.condition.wait(self.timeout)
+				self.condition.release()
+			else:
+				try:
+					rr, wr, er = select.select(rs, [], [], self.timeout)
+					for socket in rr:
+						try:
+							self.readers[socket].handle_read()
+						except:
+							traceback.print_exc()
+				except:
+					pass
+
+	def getReaders(self):
+		result = []
+		self.condition.acquire()
+		result = self.readers.keys()
+		self.condition.release()
+		return result
+	
+	def addReader(self, reader, socket):
+		self.condition.acquire()
+		self.readers[socket] = reader
+		self.condition.notify()
+		self.condition.release()
+
+	def delReader(self, socket):
+		self.condition.acquire()
+		del(self.readers[socket])
+		self.condition.notify()
+		self.condition.release()
+
+	def notify(self):
+		self.condition.acquire()
+		self.condition.notify()
+		self.condition.release()
+
+class Listener(object):
+	"""A Listener is used by this module to listen on the multicast
+	group to which DNS messages are sent, allowing the implementation
+	to cache information as it arrives.
+
+	It requires registration with an Engine object in order to have
+	the read() method called when a socket is availble for reading."""
+	
+	def __init__(self, zeroconf):
+		self.zeroconf = zeroconf
+		self.zeroconf.engine.addReader(self, self.zeroconf.socket)
+
+	def handle_read(self):
+		data, (addr, port) = self.zeroconf.socket.recvfrom(_MAX_MSG_ABSOLUTE)
+		self.data = data
+		msg = DNSIncoming(data)
+		if msg.isQuery():
+			# Always multicast responses
+			#
+			if port == _MDNS_PORT:
+				self.zeroconf.handleQuery(msg, _MDNS_ADDR, _MDNS_PORT)
+			# If it's not a multicast query, reply via unicast
+			# and multicast
+			#
+			elif port == _DNS_PORT:
+				self.zeroconf.handleQuery(msg, addr, port)
+				self.zeroconf.handleQuery(msg, _MDNS_ADDR, _MDNS_PORT)
+		else:
+			self.zeroconf.handleResponse(msg)
+
+
+class Reaper(threading.Thread):
+	"""A Reaper is used by this module to remove cache entries that
+	have expired."""
+	
+	def __init__(self, zeroconf):
+		threading.Thread.__init__(self)
+		self.zeroconf = zeroconf
+		self.start()
+
+	def run(self):
+		while 1:
+			self.zeroconf.wait(10 * 1000)
+			if globals()['_GLOBAL_DONE']:
+				return
+			now = currentTimeMillis()
+			for record in self.zeroconf.cache.entries():
+				if record.isExpired(now):
+					self.zeroconf.updateRecord(now, record)
+					self.zeroconf.cache.remove(record)
+
+
+class ServiceBrowser(threading.Thread):
+	"""Used to browse for a service of a specific type.
+
+	The listener object will have its addService() and
+	removeService() methods called when this browser
+	discovers changes in the services availability."""
+	
+	def __init__(self, zeroconf, type, listener):
+		"""Creates a browser for a specific type"""
+		threading.Thread.__init__(self)
+		self.zeroconf = zeroconf
+		self.type = type
+		self.listener = listener
+		self.services = {}
+		self.nextTime = currentTimeMillis()
+		self.delay = _BROWSER_TIME
+		self.list = []
+		
+		self.done = 0
+
+		self.zeroconf.addListener(self, DNSQuestion(self.type, _TYPE_PTR, _CLASS_IN))
+		self.start()
+
+	def updateRecord(self, zeroconf, now, record):
+		"""Callback invoked by Zeroconf when new information arrives.
+
+		Updates information required by browser in the Zeroconf cache."""
+		if record.type == _TYPE_PTR and record.name == self.type:
+			expired = record.isExpired(now)
+			try:
+				oldrecord = self.services[record.alias.lower()]
+				if not expired:
+					oldrecord.resetTTL(record)
+				else:
+					del(self.services[record.alias.lower()])
+					callback = lambda x: self.listener.removeService(x, self.type, record.alias)
+					self.list.append(callback)
+					return
+			except:
+				if not expired:
+					self.services[record.alias.lower()] = record
+					callback = lambda x: self.listener.addService(x, self.type, record.alias)
+					self.list.append(callback)
+
+			expires = record.getExpirationTime(75)
+			if expires < self.nextTime:
+				self.nextTime = expires
+
+	def cancel(self):
+		self.done = 1
+		self.zeroconf.notifyAll()
+
+	def run(self):
+		while 1:
+			event = None
+			now = currentTimeMillis()
+			if len(self.list) == 0 and self.nextTime > now:
+				self.zeroconf.wait(self.nextTime - now)
+			if globals()['_GLOBAL_DONE'] or self.done:
+				return
+			now = currentTimeMillis()
+
+			if self.nextTime <= now:
+				out = DNSOutgoing(_FLAGS_QR_QUERY)
+				out.addQuestion(DNSQuestion(self.type, _TYPE_PTR, _CLASS_IN))
+				for record in self.services.values():
+					if not record.isExpired(now):
+						out.addAnswerAtTime(record, now)
+				self.zeroconf.send(out)
+				self.nextTime = now + self.delay
+				self.delay = min(20 * 1000, self.delay * 2)
+
+			if len(self.list) > 0:
+				event = self.list.pop(0)
+
+			if event is not None:
+				event(self.zeroconf)
+				
+
+class ServiceInfo(object):
+	"""Service information"""
+	
+	def __init__(self, type, name, address=None, port=None, weight=0, priority=0, properties=None, server=None):
+		"""Create a service description.
+
+		type: fully qualified service type name
+		name: fully qualified service name
+		address: IP address as unsigned short, network byte order
+		port: port that the service runs on
+		weight: weight of the service
+		priority: priority of the service
+		properties: dictionary of properties (or a string holding the bytes for the text field)
+		server: fully qualified name for service host (defaults to name)"""
+
+		if not name.endswith(type):
+			raise BadTypeInNameException
+		self.type = type
+		self.name = name
+		self.address = address
+		self.port = port
+		self.weight = weight
+		self.priority = priority
+		if server:
+			self.server = server
+		else:
+			self.server = name
+		self.setProperties(properties)
+
+	def setProperties(self, properties):
+		"""Sets properties and text of this info from a dictionary"""
+		if isinstance(properties, dict):
+			self.properties = properties
+			list = []
+			result = ''
+			for key in properties:
+				value = properties[key]
+				if value is None:
+					suffix = ''.encode('utf-8')
+				elif isinstance(value, str):
+					suffix = value.encode('utf-8')
+				elif isinstance(value, int):
+					if value:
+						suffix = 'true'
+					else:
+						suffix = 'false'
+				else:
+					suffix = ''.encode('utf-8')
+				list.append('='.join((key, suffix)))
+			for item in list:
+				result = ''.join((result, struct.pack('!c', chr(len(item))), item))
+			self.text = result
+		else:
+			self.text = properties
+
+	def setText(self, text):
+		"""Sets properties and text given a text field"""
+		self.text = text
+		try:
+			result = {}
+			end = len(text)
+			index = 0
+			strs = []
+			while index < end:
+				length = ord(text[index])
+				index += 1
+				strs.append(text[index:index+length])
+				index += length
+			
+			for s in strs:
+				eindex = s.find('=')
+				if eindex == -1:
+					# No equals sign at all
+					key = s
+					value = 0
+				else:
+					key = s[:eindex]
+					value = s[eindex+1:]
+					if value == 'true':
+						value = 1
+					elif value == 'false' or not value:
+						value = 0
+
+				# Only update non-existent properties
+				if key and result.get(key) == None:
+					result[key] = value
+
+			self.properties = result
+		except:
+			traceback.print_exc()
+			self.properties = None
+			
+	def getType(self):
+		"""Type accessor"""
+		return self.type
+
+	def getName(self):
+		"""Name accessor"""
+		if self.type is not None and self.name.endswith("." + self.type):
+			return self.name[:len(self.name) - len(self.type) - 1]
+		return self.name
+
+	def getAddress(self):
+		"""Address accessor"""
+		return self.address
+
+	def getPort(self):
+		"""Port accessor"""
+		return self.port
+
+	def getPriority(self):
+		"""Pirority accessor"""
+		return self.priority
+
+	def getWeight(self):
+		"""Weight accessor"""
+		return self.weight
+
+	def getProperties(self):
+		"""Properties accessor"""
+		return self.properties
+
+	def getText(self):
+		"""Text accessor"""
+		return self.text
+
+	def getServer(self):
+		"""Server accessor"""
+		return self.server
+
+	def updateRecord(self, zeroconf, now, record):
+		"""Updates service information from a DNS record"""
+		if record is not None and not record.isExpired(now):
+			if record.type == _TYPE_A:
+				if record.name == self.name:
+					self.address = record.address
+			elif record.type == _TYPE_SRV:
+				if record.name == self.name:
+					self.server = record.server
+					self.port = record.port
+					self.weight = record.weight
+					self.priority = record.priority
+					self.address = None
+					self.updateRecord(zeroconf, now, zeroconf.cache.getByDetails(self.server, _TYPE_A, _CLASS_IN))
+			elif record.type == _TYPE_TXT:
+				if record.name == self.name:
+					self.setText(record.text)
+
+	def request(self, zeroconf, timeout):
+		"""Returns true if the service could be discovered on the
+		network, and updates this object with details discovered.
+		"""
+		now = currentTimeMillis()
+		delay = _LISTENER_TIME
+		next = now + delay
+		last = now + timeout
+		result = 0
+		try:
+			zeroconf.addListener(self, DNSQuestion(self.name, _TYPE_ANY, _CLASS_IN))
+			while self.server is None or self.address is None or self.text is None:
+				if last <= now:
+					return 0
+				if next <= now:
+					out = DNSOutgoing(_FLAGS_QR_QUERY)
+					out.addQuestion(DNSQuestion(self.name, _TYPE_SRV, _CLASS_IN))
+					out.addAnswerAtTime(zeroconf.cache.getByDetails(self.name, _TYPE_SRV, _CLASS_IN), now)
+					out.addQuestion(DNSQuestion(self.name, _TYPE_TXT, _CLASS_IN))
+					out.addAnswerAtTime(zeroconf.cache.getByDetails(self.name, _TYPE_TXT, _CLASS_IN), now)
+					if self.server is not None:
+						out.addQuestion(DNSQuestion(self.server, _TYPE_A, _CLASS_IN))
+						out.addAnswerAtTime(zeroconf.cache.getByDetails(self.server, _TYPE_A, _CLASS_IN), now)
+					zeroconf.send(out)
+					next = now + delay
+					delay = delay * 2
+
+				zeroconf.wait(min(next, last) - now)
+				now = currentTimeMillis()
+			result = 1
+		finally:
+			zeroconf.removeListener(self)
+			
+		return result
+
+	def __eq__(self, other):
+		"""Tests equality of service name"""
+		if isinstance(other, ServiceInfo):
+			return other.name == self.name
+		return 0
+
+	def __ne__(self, other):
+		"""Non-equality test"""
+		return not self.__eq__(other)
+
+	def __repr__(self):
+		"""String representation"""
+		result = "service[%s,%s:%s," % (self.name, socket.inet_ntoa(self.getAddress()), self.port)
+		if self.text is None:
+			result += "None"
+		else:
+			if len(self.text) < 20:
+				result += self.text
+			else:
+				result += self.text[:17] + "..."
+		result += "]"
+		return result
+				
+
+class Zeroconf(object):
+	"""Implementation of Zeroconf Multicast DNS Service Discovery
+
+	Supports registration, unregistration, queries and browsing.
+	"""
+	def __init__(self, bindaddress=None):
+		"""Creates an instance of the Zeroconf class, establishing
+		multicast communications, listening and reaping threads."""
+		globals()['_GLOBAL_DONE'] = 0
+		if bindaddress is None:
+			self.intf = socket.gethostbyname(socket.gethostname())
+		else:
+			self.intf = bindaddress
+		self.group = ('', _MDNS_PORT)
+		self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+		try:
+			self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+			self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
+		except:
+			# SO_REUSEADDR should be equivalent to SO_REUSEPORT for
+			# multicast UDP sockets (p 731, "TCP/IP Illustrated,
+			# Volume 2"), but some BSD-derived systems require
+			# SO_REUSEPORT to be specified explicity.  Also, not all
+			# versions of Python have SO_REUSEPORT available.  So
+			# if you're on a BSD-based system, and haven't upgraded
+			# to Python 2.3 yet, you may find this library doesn't
+			# work as expected.
+			#
+			pass
+		self.socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_TTL, 255)
+		self.socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, 1)
+		try:
+			self.socket.bind(self.group)
+		except:
+			# Some versions of linux raise an exception even though
+			# the SO_REUSE* options have been set, so ignore it
+			#
+			pass
+		self.socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_IF, socket.inet_aton(self.intf) + socket.inet_aton('0.0.0.0'))
+		self.socket.setsockopt(socket.SOL_IP, socket.IP_ADD_MEMBERSHIP, socket.inet_aton(_MDNS_ADDR) + socket.inet_aton('0.0.0.0'))
+
+		self.listeners = []
+		self.browsers = []
+		self.services = {}
+
+		self.cache = DNSCache()
+
+		self.condition = threading.Condition()
+		
+		self.engine = Engine(self)
+		self.listener = Listener(self)
+		self.reaper = Reaper(self)
+
+	def isLoopback(self):
+		return self.intf.startswith("127.0.0.1")
+
+	def isLinklocal(self):
+		return self.intf.startswith("169.254.")
+
+	def wait(self, timeout):
+		"""Calling thread waits for a given number of milliseconds or
+		until notified."""
+		self.condition.acquire()
+		self.condition.wait(timeout/1000)
+		self.condition.release()
+
+	def notifyAll(self):
+		"""Notifies all waiting threads"""
+		self.condition.acquire()
+		self.condition.notifyAll()
+		self.condition.release()
+
+	def getServiceInfo(self, type, name, timeout=3000):
+		"""Returns network's service information for a particular
+		name and type, or None if no service matches by the timeout,
+		which defaults to 3 seconds."""
+		info = ServiceInfo(type, name)
+		if info.request(self, timeout):
+			return info
+		return None
+
+	def addServiceListener(self, type, listener):
+		"""Adds a listener for a particular service type.  This object
+		will then have its updateRecord method called when information
+		arrives for that type."""
+		self.removeServiceListener(listener)
+		self.browsers.append(ServiceBrowser(self, type, listener))
+
+	def removeServiceListener(self, listener):
+		"""Removes a listener from the set that is currently listening."""
+		for browser in self.browsers:
+			if browser.listener == listener:
+				browser.cancel()
+				del(browser)
+
+	def registerService(self, info, ttl=_DNS_TTL):
+		"""Registers service information to the network with a default TTL
+		of 60 seconds.  Zeroconf will then respond to requests for
+		information for that service.  The name of the service may be
+		changed if needed to make it unique on the network."""
+		self.checkService(info)
+		self.services[info.name.lower()] = info
+		now = currentTimeMillis()
+		nextTime = now
+		i = 0
+		while i < 3:
+			if now < nextTime:
+				self.wait(nextTime - now)
+				now = currentTimeMillis()
+				continue
+			out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA)
+			out.addAnswerAtTime(DNSPointer(info.type, _TYPE_PTR, _CLASS_IN, ttl, info.name), 0)
+			out.addAnswerAtTime(DNSService(info.name, _TYPE_SRV, _CLASS_IN, ttl, info.priority, info.weight, info.port, info.server), 0)
+			out.addAnswerAtTime(DNSText(info.name, _TYPE_TXT, _CLASS_IN, ttl, info.text), 0)
+			if info.address:
+				out.addAnswerAtTime(DNSAddress(info.server, _TYPE_A, _CLASS_IN, ttl, info.address), 0)
+			self.send(out)
+			i += 1
+			nextTime += _REGISTER_TIME
+
+	def unregisterService(self, info):
+		"""Unregister a service."""
+		try:
+			del(self.services[info.name.lower()])
+		except:
+			pass
+		now = currentTimeMillis()
+		nextTime = now
+		i = 0
+		while i < 3:
+			if now < nextTime:
+				self.wait(nextTime - now)
+				now = currentTimeMillis()
+				continue
+			out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA)
+			out.addAnswerAtTime(DNSPointer(info.type, _TYPE_PTR, _CLASS_IN, 0, info.name), 0)
+			out.addAnswerAtTime(DNSService(info.name, _TYPE_SRV, _CLASS_IN, 0, info.priority, info.weight, info.port, info.name), 0)
+			out.addAnswerAtTime(DNSText(info.name, _TYPE_TXT, _CLASS_IN, 0, info.text), 0)
+			if info.address:
+				out.addAnswerAtTime(DNSAddress(info.server, _TYPE_A, _CLASS_IN, 0, info.address), 0)
+			self.send(out)
+			i += 1
+			nextTime += _UNREGISTER_TIME
+
+	def unregisterAllServices(self):
+		"""Unregister all registered services."""
+		if len(self.services) > 0:
+			now = currentTimeMillis()
+			nextTime = now
+			i = 0
+			while i < 3:
+				if now < nextTime:
+					self.wait(nextTime - now)
+					now = currentTimeMillis()
+					continue
+				out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA)
+				for info in self.services.values():
+					out.addAnswerAtTime(DNSPointer(info.type, _TYPE_PTR, _CLASS_IN, 0, info.name), 0)
+					out.addAnswerAtTime(DNSService(info.name, _TYPE_SRV, _CLASS_IN, 0, info.priority, info.weight, info.port, info.server), 0)
+					out.addAnswerAtTime(DNSText(info.name, _TYPE_TXT, _CLASS_IN, 0, info.text), 0)
+					if info.address:
+						out.addAnswerAtTime(DNSAddress(info.server, _TYPE_A, _CLASS_IN, 0, info.address), 0)
+				self.send(out)
+				i += 1
+				nextTime += _UNREGISTER_TIME
+
+	def checkService(self, info):
+		"""Checks the network for a unique service name, modifying the
+		ServiceInfo passed in if it is not unique."""
+		now = currentTimeMillis()
+		nextTime = now
+		i = 0
+		while i < 3:
+			for record in self.cache.entriesWithName(info.type):
+				if record.type == _TYPE_PTR and not record.isExpired(now) and record.alias == info.name:
+					if (info.name.find('.') < 0):
+						info.name = info.name + ".[" + info.address + ":" + info.port + "]." + info.type
+						self.checkService(info)
+						return
+					raise NonUniqueNameException
+			if now < nextTime:
+				self.wait(nextTime - now)
+				now = currentTimeMillis()
+				continue
+			out = DNSOutgoing(_FLAGS_QR_QUERY | _FLAGS_AA)
+			self.debug = out
+			out.addQuestion(DNSQuestion(info.type, _TYPE_PTR, _CLASS_IN))
+			out.addAuthorativeAnswer(DNSPointer(info.type, _TYPE_PTR, _CLASS_IN, _DNS_TTL, info.name))
+			self.send(out)
+			i += 1
+			nextTime += _CHECK_TIME
+
+	def addListener(self, listener, question):
+		"""Adds a listener for a given question.  The listener will have
+		its updateRecord method called when information is available to
+		answer the question."""
+		now = currentTimeMillis()
+		self.listeners.append(listener)
+		if question is not None:
+			for record in self.cache.entriesWithName(question.name):
+				if question.answeredBy(record) and not record.isExpired(now):
+					listener.updateRecord(self, now, record)
+		self.notifyAll()
+
+	def removeListener(self, listener):
+		"""Removes a listener."""
+		try:
+			self.listeners.remove(listener)
+			self.notifyAll()
+		except:
+			pass
+
+	def updateRecord(self, now, rec):
+		"""Used to notify listeners of new information that has updated
+		a record."""
+		for listener in self.listeners:
+			listener.updateRecord(self, now, rec)
+		self.notifyAll()
+
+	def handleResponse(self, msg):
+		"""Deal with incoming response packets.  All answers
+		are held in the cache, and listeners are notified."""
+		now = currentTimeMillis()
+		for record in msg.answers:
+			expired = record.isExpired(now)
+			if record in self.cache.entries():
+				if expired:
+					self.cache.remove(record)
+				else:
+					entry = self.cache.get(record)
+					if entry is not None:
+						entry.resetTTL(record)
+						record = entry
+			else:
+				self.cache.add(record)
+				
+			self.updateRecord(now, record)
+
+	def handleQuery(self, msg, addr, port):
+		"""Deal with incoming query packets.  Provides a response if
+		possible."""
+		out = None
+
+		# Support unicast client responses
+		#
+		if port != _MDNS_PORT:
+			out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA, 0)
+			for question in msg.questions:
+				out.addQuestion(question)
+		
+		for question in msg.questions:
+			if question.type == _TYPE_PTR:
+				for service in self.services.values():
+					if question.name == service.type:
+						if out is None:
+							out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA)
+						out.addAnswer(msg, DNSPointer(service.type, _TYPE_PTR, _CLASS_IN, _DNS_TTL, service.name))
+			else:
+				try:
+					if out is None:
+						out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA)
+					
+					# Answer A record queries for any service addresses we know
+					if question.type == _TYPE_A or question.type == _TYPE_ANY:
+						for service in self.services.values():
+							if service.server == question.name.lower():
+								out.addAnswer(msg, DNSAddress(question.name, _TYPE_A, _CLASS_IN | _CLASS_UNIQUE, _DNS_TTL, service.address))
+					
+					service = self.services.get(question.name.lower(), None)
+					if not service: continue
+					
+					if question.type == _TYPE_SRV or question.type == _TYPE_ANY:
+						out.addAnswer(msg, DNSService(question.name, _TYPE_SRV, _CLASS_IN | _CLASS_UNIQUE, _DNS_TTL, service.priority, service.weight, service.port, service.server))
+					if question.type == _TYPE_TXT or question.type == _TYPE_ANY:
+						out.addAnswer(msg, DNSText(question.name, _TYPE_TXT, _CLASS_IN | _CLASS_UNIQUE, _DNS_TTL, service.text))
+					if question.type == _TYPE_SRV:
+						out.addAdditionalAnswer(DNSAddress(service.server, _TYPE_A, _CLASS_IN | _CLASS_UNIQUE, _DNS_TTL, service.address))
+				except:
+					traceback.print_exc()
+				
+		if out is not None and out.answers:
+			out.id = msg.id
+			self.send(out, addr, port)
+
+	def send(self, out, addr = _MDNS_ADDR, port = _MDNS_PORT):
+		"""Sends an outgoing packet."""
+		# This is a quick test to see if we can parse the packets we generate
+		#temp = DNSIncoming(out.packet())
+		try:
+			bytes_sent = self.socket.sendto(out.packet(), 0, (addr, port))
+		except:
+			# Ignore this, it may be a temporary loss of network connection
+			pass
+
+	def close(self):
+		"""Ends the background threads, and prevent this instance from
+		servicing further queries."""
+		if globals()['_GLOBAL_DONE'] == 0:
+			globals()['_GLOBAL_DONE'] = 1
+			self.notifyAll()
+			self.engine.notify()
+			self.unregisterAllServices()
+			self.socket.setsockopt(socket.SOL_IP, socket.IP_DROP_MEMBERSHIP, socket.inet_aton(_MDNS_ADDR) + socket.inet_aton('0.0.0.0'))
+			self.socket.close()
+			
+# Test a few module features, including service registration, service
+# query (for Zoe), and service unregistration.
+
+if __name__ == '__main__':	
+	print "Multicast DNS Service Discovery for Python, version", __version__
+	r = Zeroconf()
+	print "1. Testing registration of a service..."
+	desc = {'version':'0.10','a':'test value', 'b':'another value'}
+	info = ServiceInfo("_http._tcp.local.", "My Service Name._http._tcp.local.", socket.inet_aton("127.0.0.1"), 1234, 0, 0, desc)
+	print "   Registering service..."
+	r.registerService(info)
+	print "   Registration done."
+	print "2. Testing query of service information..."
+	print "   Getting ZOE service:", str(r.getServiceInfo("_http._tcp.local.", "ZOE._http._tcp.local."))
+	print "   Query done."
+	print "3. Testing query of own service..."
+	print "   Getting self:", str(r.getServiceInfo("_http._tcp.local.", "My Service Name._http._tcp.local."))
+	print "   Query done."
+	print "4. Testing unregister of service information..."
+	r.unregisterService(info)
+	print "   Unregister done."
+	r.close()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/connectors/PYRO/__init__.py	Wed Aug 20 00:11:40 2008 +0200
@@ -0,0 +1,106 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
+#
+#See COPYING file for copyrights details.
+#
+#This library is free software; you can redistribute it and/or
+#modify it under the terms of the GNU General Public
+#License as published by the Free Software Foundation; either
+#version 2.1 of the License, or (at your option) any later version.
+#
+#This library is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#General Public License for more details.
+#
+#You should have received a copy of the GNU General Public
+#License along with this library; if not, write to the Free Software
+#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+import Pyro.core as pyro
+from Pyro.errors import PyroError
+import traceback
+from time import sleep
+
+def PYRO_connector_factory(uri, pluginsroot):
+    """
+    This returns the connector to Pyro style PLCobject
+    """
+    pluginsroot.logger.write("Connecting to URI : %s\n"%uri)
+
+    servicetype, location = uri.split("://")
+    
+    # Try to get the proxy object
+    try :
+        RemotePLCObjectProxy = pyro.getAttrProxyForURI("PYROLOC://"+location+"/PLCObject")
+    except Exception, msg:
+        pluginsroot.logger.write_error("Wrong URI, please check it !\n")
+        pluginsroot.logger.write_error(traceback.format_exc())
+        return None
+
+    def PyroCatcher(func, default=None):
+        """
+        A function that catch a pyro exceptions, write error to logger
+        and return defaul value when it happen
+        """
+        def cather_func(*args,**kwargs):
+            try:
+                return func(*args,**kwargs)
+            except PyroError,e:
+                #pluginsroot.logger.write_error(traceback.format_exc())
+                pluginsroot.logger.write_error(str(e))
+                pluginsroot._Disconnect()
+                return default
+        return cather_func
+
+    # Check connection is effective. 
+    # lambda is for getattr of GetPLCstatus to happen inside catcher
+    if PyroCatcher(lambda:RemotePLCObjectProxy.GetPLCstatus())() == None:
+        pluginsroot.logger.write_error("Cannot get PLC status - connection failed.\n")
+        return None
+        
+    class PyroProxyProxy:
+        """
+        A proxy proxy class to handle Beremiz Pyro interface specific behavior.
+        And to put pyro exception catcher in between caller and pyro proxy
+        """
+        def GetPyroProxy(self):
+            """
+            This func returns the real Pyro Proxy.
+            Use this if you musn't keep reference to it.
+            """
+            return RemotePLCObjectProxy
+        
+        def __getattr__(self, attrName):
+            if not self.__dict__.has_key(attrName):
+                if attrName=="StartPLC":
+                    def _StartPLC():
+                        """
+                        pluginsroot._connector.GetPyroProxy() is used 
+                        rather than RemotePLCObjectProxy because
+                        object is recreated meanwhile, 
+                        so we must not keep ref to it here
+                        """
+                        if pluginsroot._connector.GetPyroProxy().GetPLCstatus() == "Dirty":
+                            """
+                            Some bad libs with static symbols may polute PLC
+                            ask runtime to suicide and come back again
+                            """
+                            pluginsroot.logger.write("Force runtime reload\n")
+                            pluginsroot._connector.GetPyroProxy().ForceReload()
+                            pluginsroot._Disconnect()
+                            # let remote PLC time to resurect.(freeze app)
+                            sleep(0.5)
+                            pluginsroot._Connect()
+                        return pluginsroot._connector.GetPyroProxy().StartPLC()
+                    member = PyroCatcher(_StartPLC, False)
+                else:
+                    def my_local_func(*args,**kwargs):
+                        return RemotePLCObjectProxy.__getattr__(attrName)(*args,**kwargs)
+                    member = PyroCatcher(my_local_func, None)
+                self.__dict__[attrName] = member
+            return self.__dict__[attrName]
+    return PyroProxyProxy()
+    
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/connectors/USB/__init__.py	Wed Aug 20 00:11:40 2008 +0200
@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
+#
+#See COPYING file for copyrights details.
+#
+#This library is free software; you can redistribute it and/or
+#modify it under the terms of the GNU General Public
+#License as published by the Free Software Foundation; either
+#version 2.1 of the License, or (at your option) any later version.
+#
+#This library is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#General Public License for more details.
+#
+#You should have received a copy of the GNU General Public
+#License along with this library; if not, write to the Free Software
+#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+from connector_USB import *
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/connectors/__init__.py	Wed Aug 20 00:11:40 2008 +0200
@@ -0,0 +1,43 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
+#
+#See COPYING file for copyrights details.
+#
+#This library is free software; you can redistribute it and/or
+#modify it under the terms of the GNU General Public
+#License as published by the Free Software Foundation; either
+#version 2.1 of the License, or (at your option) any later version.
+#
+#This library is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#General Public License for more details.
+#
+#You should have received a copy of the GNU General Public
+#License along with this library; if not, write to the Free Software
+#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+# Package initialisation
+
+from os import listdir, path
+
+_base_path = path.split(__file__)[0]
+
+connector_types = [name for name in listdir(_base_path) if path.isdir(path.join(_base_path, name)) and name.upper() != "CVS" and not name.startswith("__")]
+
+def ConnectorFactory(uri, pluginsroot):
+    """
+    Return a connector corresponding to the URI
+    or None if cannot connect to URI
+    """
+    servicetype = uri.split("://")[0]
+    if servicetype in connector_types:
+        # import module according to uri type
+        connectormodule = getattr(__import__("connectors."+servicetype), servicetype)
+        factoryname = servicetype + "_connector_factory"
+        return getattr(connectormodule, factoryname)(uri, pluginsroot)
+    else :
+        return None    
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/discovery.py	Wed Aug 20 00:11:40 2008 +0200
@@ -0,0 +1,171 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+#This file is part of Beremiz, a Integrated Development Environment for
+#programming IEC 61131-3 automates supporting plcopen standard and CanFestival. 
+#
+#Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
+#
+#See COPYING file for copyrights details.
+#
+#This library is free software; you can redistribute it and/or
+#modify it under the terms of the GNU General Public
+#License as published by the Free Software Foundation; either
+#version 2.1 of the License, or (at your option) any later version.
+#
+#This library is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#General Public License for more details.
+#
+#You should have received a copy of the GNU General Public
+#License along with this library; if not, write to the Free Software
+#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+import wx
+from Zeroconf import *
+import socket
+import  wx.lib.mixins.listctrl  as  listmix
+
+class TestListCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin):
+    def __init__(self, parent, ID, pos=wx.DefaultPosition,
+                 size=wx.DefaultSize, style=0):
+        wx.ListCtrl.__init__(self, parent, ID, pos, size, style)
+        listmix.ListCtrlAutoWidthMixin.__init__(self)
+
+class DiscoveryDialog(wx.Dialog, listmix.ColumnSorterMixin):
+    def __init__(self, parent, id=-1, title='Service Discovery'):
+        self.my_result=None
+        self.itemDataMap = {}
+        wx.Dialog.__init__(self, parent, id, title, size=(600,600), style=wx.DEFAULT_DIALOG_STYLE)
+
+        self.list = TestListCtrl(self, -1,
+                                 pos=(50,20), 
+                                 size=(500,300),
+                                 style=wx.LC_REPORT 
+                                | wx.LC_EDIT_LABELS
+                                | wx.LC_SORT_ASCENDING
+                                )
+        self.PopulateList()
+
+        self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected, self.list)
+        self.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.OnItemDeselected, self.list)
+        self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnItemActivated, self.list)
+        self.Bind(wx.EVT_LIST_DELETE_ITEM, self.OnItemDelete, self.list)
+        self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColClick, self.list)
+        self.list.Bind(wx.EVT_LEFT_DCLICK, self.OnDoubleClick)
+
+        b = wx.Button(self,20, "Connect", (175, 500))
+        self.Bind(wx.EVT_BUTTON, self.OnConnect, b)
+        b.SetSize(b.GetBestSize())
+
+        b = wx.Button(self, 40, "Cancel", (350, 500))
+        self.Bind(wx.EVT_BUTTON, self.OnClose, b)
+        b.SetSize(b.GetBestSize())
+
+        #type = "_http._tcp.local."
+        type = "_PYRO._tcp.local."
+        self.r = Zeroconf()	
+        browser = ServiceBrowser(self.r, type, self)		
+
+        listmix.ColumnSorterMixin.__init__(self, 4)
+    # Used by the ColumnSorterMixin, see wx/lib/mixins/listctrl.py
+    def GetListCtrl(self):
+        return self.list
+
+    def PopulateList(self):
+ 	self.list.InsertColumn(0, 'NAME')
+        self.list.InsertColumn(1, 'TYPE')
+        self.list.InsertColumn(2, 'IP')
+        self.list.InsertColumn(3, 'PORT')
+        self.list.SetColumnWidth(0, 150)
+        self.list.SetColumnWidth(1, 150)
+        self.list.SetColumnWidth(2, 150)
+        self.list.SetColumnWidth(3, 150)
+
+    def getColumnText(self, index, col):
+        item = self.list.GetItem(index, col)
+        return item.GetText()
+
+    def OnItemSelected(self, event):
+        self.currentItem = event.m_itemIndex
+        print "OnItemSelected: %s, %s, %s, %s\n"%(self.currentItem,
+                            self.list.GetItemText(self.currentItem),
+                            self.getColumnText(self.currentItem, 1),
+                            self.getColumnText(self.currentItem, 2))
+        event.Skip()
+
+
+    def OnItemDeselected(self, evt):
+        item = evt.GetItem()
+        print "OnItemDeselected: %d" % evt.m_itemIndex
+
+    def OnItemActivated(self, event):
+        self.currentItem = event.m_itemIndex
+        print "OnItemActivated: %s\nTopItem: %s" %(self.list.GetItemText(self.currentItem), self.list.GetTopItem())
+
+    def OnItemDelete(self, event):
+        print "OnItemDelete\n"
+
+    def OnColClick(self, event):
+        print "OnColClick: %d\n" % event.GetColumn()
+        event.Skip()
+
+    def OnColRightClick(self, event):
+        item = self.list.GetColumn(event.GetColumn())
+        print "OnColRightClick: %d %s\n" %(event.GetColumn(), (item.GetText(), item.GetAlign(),
+                                                item.GetWidth(), item.GetImage()))
+    def OnDoubleClick(self, event):
+        connect_type = self.getColumnText(self.currentItem, 1)
+        connect_address = self.getColumnText(self.currentItem, 2)
+        connect_port = self.getColumnText(self.currentItem, 3)
+        
+        uri = self.CreateURI(connect_type, connect_address, connect_port)
+        self.my_result=uri
+        event.Skip()
+
+    def GetResult(self):
+        return self.my_result
+        
+    def OnClick(self, event):
+        print "Click! (%d)\n" %event.GetId()
+        index = self.list.GetFocusedItem()
+        self.list.DeleteItem(index)	
+        print "Service", name, "removed"
+
+    def removeService(self, zeroconf, type, name):
+        index = self.list.GetFocusedItem()       
+	
+    def addService(self, zeroconf, type, name):
+        info = self.r.getServiceInfo(type, name)
+        typename = type.split(".")[0][1:]
+        num_items = self.list.GetItemCount()
+        self.itemDataMap[num_items] = (name, "%s"%type, "%s"%str(socket.inet_ntoa(info.getAddress())), "%s"%info.getPort())
+        self.list.InsertStringItem(num_items, name.split(".")[0])
+        self.list.SetStringItem(num_items, 1, "%s"%typename)
+        self.list.SetStringItem(num_items, 2, "%s"%str(socket.inet_ntoa(info.getAddress())))
+        self.list.SetStringItem(num_items, 3, "%s"%info.getPort())
+
+    def CreateURI(self, connect_type, connect_address, connect_port):
+        uri = "%s://%s:%s"%(connect_type, connect_address, connect_port)
+        print uri
+        return uri
+
+    def OnAdd(self, event):
+        num_items = self.list.GetItemCount()
+        self.list.InsertStringItem(num_items, self.tc1.GetValue())
+        self.list.SetStringItem(num_items, 1, self.tc2.GetValue())
+
+    def OnRemove(self, event):
+        index = self.list.GetFocusedItem()
+        self.list.DeleteItem(index)
+
+    def OnConnect(self, event):
+        index = self.list.GetFocusedItem()
+        print self.list.GetItemData(index)
+
+    def OnClose(self, event):
+        self.Close()
+
+    def OnClear(self, event):
+        self.list.DeleteAllItems()
Binary file images/Connect.png has changed
Binary file images/Debug.png has changed
Binary file images/Disconnect.png has changed
Binary file images/Transfer.png has changed
--- a/images/icons.svg	Tue Aug 12 16:27:07 2008 +0200
+++ b/images/icons.svg	Wed Aug 20 00:11:40 2008 +0200
@@ -30,7 +30,7 @@
   </metadata>
   <sodipodi:namedview
      inkscape:window-height="994"
-     inkscape:window-width="1625"
+     inkscape:window-width="1623"
      inkscape:pageshadow="2"
      inkscape:pageopacity="0.0"
      guidetolerance="10.0"
@@ -40,12 +40,12 @@
      bordercolor="#666666"
      pagecolor="#ffffff"
      id="base"
-     showgrid="true"
+     showgrid="false"
      inkscape:zoom="1"
-     inkscape:cx="440.43263"
-     inkscape:cy="865.35999"
-     inkscape:window-x="47"
-     inkscape:window-y="25"
+     inkscape:cx="514.46278"
+     inkscape:cy="430.8377"
+     inkscape:window-x="52"
+     inkscape:window-y="51"
      inkscape:current-layer="svg2"
      showguides="true"
      inkscape:guide-bbox="true">
@@ -56,6 +56,30 @@
   <defs
      id="defs4">
     <linearGradient
+       inkscape:collect="always"
+       id="linearGradient17546">
+      <stop
+         style="stop-color:#ff0000;stop-opacity:1;"
+         offset="0"
+         id="stop17548" />
+      <stop
+         style="stop-color:#ffff00;stop-opacity:1"
+         offset="1"
+         id="stop17550" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient17526">
+      <stop
+         style="stop-color:#469837;stop-opacity:1;"
+         offset="0"
+         id="stop17528" />
+      <stop
+         style="stop-color:#469837;stop-opacity:0;"
+         offset="1"
+         id="stop17530" />
+    </linearGradient>
+    <linearGradient
        id="linearGradient2345">
       <stop
          style="stop-color:#ffffff;stop-opacity:1.0000000;"
@@ -82531,10 +82555,116 @@
        y1="221.98289"
        x2="46.488174"
        y2="259.94464" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient34137"
+       id="linearGradient16478"
+       gradientUnits="userSpaceOnUse"
+       x1="-77.844841"
+       y1="5.1423945"
+       x2="-77.844841"
+       y2="14.276564" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient34137"
+       id="linearGradient16480"
+       gradientUnits="userSpaceOnUse"
+       x1="-77.844841"
+       y1="5.1423945"
+       x2="-77.844841"
+       y2="14.276564" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient34137"
+       id="linearGradient16596"
+       gradientUnits="userSpaceOnUse"
+       x1="-77.844841"
+       y1="5.1423945"
+       x2="-77.844841"
+       y2="14.276564" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient34137"
+       id="linearGradient16598"
+       gradientUnits="userSpaceOnUse"
+       x1="-77.844841"
+       y1="5.1423945"
+       x2="-77.844841"
+       y2="14.276564" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient34137"
+       id="linearGradient16715"
+       gradientUnits="userSpaceOnUse"
+       x1="-77.844841"
+       y1="5.1423945"
+       x2="-77.844841"
+       y2="14.276564" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient34137"
+       id="linearGradient16717"
+       gradientUnits="userSpaceOnUse"
+       x1="-77.844841"
+       y1="5.1423945"
+       x2="-77.844841"
+       y2="14.276564" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient17526"
+       id="linearGradient17532"
+       x1="335"
+       y1="137.36218"
+       x2="335"
+       y2="144.96741"
+       gradientUnits="userSpaceOnUse"
+       spreadMethod="reflect"
+       gradientTransform="translate(60,0)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient17526"
+       id="linearGradient17534"
+       x1="335"
+       y1="137.36218"
+       x2="335"
+       y2="144.96741"
+       gradientUnits="userSpaceOnUse"
+       spreadMethod="reflect"
+       gradientTransform="translate(60,0)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient17526"
+       id="linearGradient17536"
+       x1="335"
+       y1="137.36218"
+       x2="335"
+       y2="144.96741"
+       gradientUnits="userSpaceOnUse"
+       spreadMethod="reflect"
+       gradientTransform="translate(60,0)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient17526"
+       id="linearGradient17540"
+       gradientUnits="userSpaceOnUse"
+       spreadMethod="reflect"
+       x1="335"
+       y1="137.36218"
+       x2="335"
+       y2="144.96741" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient17546"
+       id="linearGradient17552"
+       x1="329.43661"
+       y1="145.7263"
+       x2="336.79922"
+       y2="141.47549"
+       gradientUnits="userSpaceOnUse" />
   </defs>
   <g
      id="g19063"
-     transform="matrix(0.9968636,0,0,0.9968648,-6.725278,-12.08626)">
+     transform="matrix(0.9968636,0,0,0.9968648,-6.725278,-192.08626)">
     <rect
        width="24.075478"
        height="24.075478"
@@ -82637,7 +82767,7 @@
     </g>
   </g>
   <g
-     transform="matrix(1.2234367,0,0,1.2234367,1268.4713,-201.46094)"
+     transform="matrix(1.2234367,0,0,1.2234367,1268.4713,-281.46094)"
      id="g17987">
     <g
        style="display:inline"
@@ -82695,21 +82825,21 @@
      style="font-size:12.76095104px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans"
      xml:space="preserve"
      id="text2713"
-     y="241.52583"
+     y="181.52583"
      x="33.295933"><tspan
-       y="241.52583"
+       y="181.52583"
        x="33.295933"
        id="tspan16193"
        sodipodi:role="line">%% Build Clean editPLC HMIEditor ImportDEF ImportSVG NetworkEdit Run ShowIECcode Stop Unknown %%</tspan></text>
   <rect
      style="fill:#000000;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
      id="Unknown"
-     y="271.36218"
+     y="191.36218"
      x="660"
      height="24"
      width="24" />
   <g
-     transform="translate(1181,-139.4776)"
+     transform="translate(1181,-219.4776)"
      id="g16213">
     <rect
        width="24"
@@ -82773,7 +82903,7 @@
     </g>
   </g>
   <g
-     transform="translate(1240.7988,-169.49646)"
+     transform="translate(1240.7988,-249.49646)"
      id="g16489">
     <g
        id="g16229">
@@ -82869,7 +82999,7 @@
        d="M -1045.6271,459.85458 L -1044.4626,460.28154 L -1044.4534,460.25632 L -1045.6179,459.82936 L -1045.6271,459.85458 z M -1045.483,459.46147 L -1044.8985,459.67576 L -1044.8893,459.65054 L -1045.4737,459.43625 L -1045.483,459.46147 z M -1045.3394,459.06983 L -1044.7549,459.28413 L -1044.7451,459.25743 L -1045.3296,459.04313 L -1045.3394,459.06983 z M -1045.1953,458.67671 L -1044.6108,458.89101 L -1044.6015,458.86579 L -1045.186,458.6515 L -1045.1953,458.67671 z M -1045.0511,458.2836 L -1044.4666,458.49789 L -1044.4574,458.47267 L -1045.0419,458.25838 L -1045.0511,458.2836 z M -1044.907,457.89048 L -1043.7425,458.31744 L -1043.7332,458.29222 L -1044.8977,457.86526 L -1044.907,457.89048 z M -1044.7629,457.49736 L -1044.1784,457.71166 L -1044.1691,457.68644 L -1044.7536,457.47214 L -1044.7629,457.49736 z M -1044.6193,457.10573 L -1044.0348,457.32003 L -1044.025,457.29332 L -1044.6095,457.07903 L -1044.6193,457.10573 z M -1044.4751,456.71261 L -1043.8907,456.92691 L -1043.8814,456.90169 L -1044.4659,456.68739 L -1044.4751,456.71261 z M -1044.331,456.3195 L -1043.7465,456.53379 L -1043.7373,456.50857 L -1044.3218,456.29428 L -1044.331,456.3195 z M -1044.1869,455.92638 L -1043.0224,456.35334 L -1043.0131,456.32812 L -1044.1776,455.90116 L -1044.1869,455.92638 z M -1044.0427,455.53326 L -1043.4583,455.74756 L -1043.449,455.72234 L -1044.0335,455.50804 L -1044.0427,455.53326 z M -1043.8986,455.14014 L -1043.3141,455.35444 L -1043.3049,455.32922 L -1043.8894,455.11493 L -1043.8986,455.14014 z M -1043.755,454.74851 L -1043.1705,454.96281 L -1043.1607,454.9361 L -1043.7452,454.72181 L -1043.755,454.74851 z M -1043.6109,454.35539 L -1043.0264,454.56969 L -1043.0172,454.54447 L -1043.6016,454.33017 L -1043.6109,454.35539 z M -1043.4667,453.96228 L -1042.3022,454.38924 L -1042.293,454.36402 L -1043.4575,453.93706 L -1043.4667,453.96228 z M -1043.3226,453.56916 L -1042.7381,453.78346 L -1042.7289,453.75824 L -1043.3134,453.54394 L -1043.3226,453.56916 z M -1043.1785,453.17604 L -1042.594,453.39034 L -1042.5848,453.36512 L -1043.1692,453.15082 L -1043.1785,453.17604 z M -1043.0349,452.78441 L -1042.4504,452.9987 L -1042.4406,452.972 L -1043.0251,452.75771 L -1043.0349,452.78441 z M -1042.8908,452.39129 L -1042.3063,452.60559 L -1042.297,452.58037 L -1042.8815,452.36607 L -1042.8908,452.39129 z M -1042.7466,451.99817 L -1041.5821,452.42513 L -1041.5729,452.39992 L -1042.7374,451.97295 L -1042.7466,451.99817 z M -1042.6025,451.60506 L -1042.018,451.81935 L -1042.0088,451.79413 L -1042.5932,451.57984 L -1042.6025,451.60506 z M -1042.4584,451.21194 L -1041.8739,451.42624 L -1041.8646,451.40102 L -1042.4491,451.18672 L -1042.4584,451.21194 z M -1042.3148,450.82031 L -1041.7303,451.0346 L -1041.7205,451.0079 L -1042.305,450.7936 L -1042.3148,450.82031 z M -1042.1706,450.42719 L -1041.5862,450.64148 L -1041.5769,450.61627 L -1042.1614,450.40197 L -1042.1706,450.42719 z M -1042.0265,450.03407 L -1040.862,450.46103 L -1040.8527,450.43581 L -1042.0173,450.00885 L -1042.0265,450.03407 z M -1041.8824,449.64095 L -1041.2979,449.85525 L -1041.2886,449.83003 L -1041.8731,449.61573 L -1041.8824,449.64095 z M -1041.7382,449.24784 L -1041.1538,449.46213 L -1041.1445,449.43692 L -1041.729,449.22262 L -1041.7382,449.24784 z M -1041.5946,448.8562 L -1041.0102,449.0705 L -1041.0004,449.0438 L -1041.5849,448.8295 L -1041.5946,448.8562 z M -1041.4505,448.46309 L -1040.866,448.67738 L -1040.8568,448.65216 L -1041.4413,448.43787 L -1041.4505,448.46309 z M -1041.3064,448.06997 L -1040.1419,448.49693 L -1040.1326,448.47171 L -1041.2971,448.04475 L -1041.3064,448.06997 z" />
   </g>
   <g
-     transform="translate(1541.0897,-320.03854)"
+     transform="translate(1541.0897,-400.03854)"
      id="g16620">
     <g
        id="g16343">
@@ -82887,7 +83017,7 @@
        d="M -1043.3148,591.51852 C -1042.1671,591.51852 -1041.2381,592.51591 -1041.2381,593.74961 C -1041.2381,594.98052 -1042.1671,595.97828 -1043.3148,595.97828 C -1044.4596,595.97828 -1045.3867,594.98052 -1045.3867,593.74961 C -1045.3867,592.51591 -1044.4596,591.51852 -1043.3148,591.51852 z M -1048.4282,594.44426 C -1047.4919,594.46171 -1046.5801,594.73882 -1046.0331,595.02798 C -1045.4412,595.33917 -1045.4725,595.99227 -1045.1141,596.27013 C -1044.75,596.52298 -1044.2166,596.30364 -1043.8888,596.59817 C -1043.5554,596.88714 -1043.3383,597.38735 -1043.1411,598.02366 C -1042.941,598.65718 -1043.2194,599.51581 -1042.6997,600.36329 C -1042.1579,601.20798 -1041.1693,601.98435 -1039.9838,603.03094 C -1039.7383,603.24774 -1039.4784,603.67038 -1039.5738,603.94508 C -1039.7674,604.50302 -1040.1089,604.66757 -1040.459,604.7121 C -1040.8091,604.74545 -1041.1452,604.49515 -1041.6481,604.16458 C -1042.14,603.81725 -1042.8525,603.29754 -1043.4137,602.70291 C -1043.9722,602.10273 -1044.4846,601.4192 -1044.979,600.61895 L -1046.3032,602.37247 C -1045.4751,603.03656 -1044.7728,603.68364 -1044.1614,604.34789 C -1043.5363,605.00904 -1042.891,605.58171 -1042.6659,606.32089 C -1042.4436,607.04889 -1042.5963,607.49919 -1042.8686,608.73288 C -1043.1436,609.97493 -1043.8931,612.61785 -1044.2989,613.70159 C -1044.6934,614.763 -1044.8709,614.98822 -1045.2516,615.12708 C -1045.6323,615.25775 -1046.3979,615.17954 -1046.5758,614.50719 C -1046.7396,613.82374 -1046.4196,612.25651 -1046.2695,611.07011 C -1046.1167,609.87237 -1045.929,608.683 -1045.693,607.41594 L -1048.4475,605.33439 C -1050.0729,607.62951 -1051.3606,609.44427 -1052.3887,610.85301 C -1053.4056,612.24789 -1053.9336,613.01256 -1054.5281,613.70159 C -1055.1089,614.38782 -1055.4351,614.80205 -1055.8909,614.94375 C -1056.3466,615.06872 -1057.1123,614.96846 -1057.2151,614.47102 C -1057.2957,613.96539 -1057.1611,613.44582 -1056.3998,611.94807 C -1055.6162,610.42825 -1054.0144,607.99671 -1052.6251,605.47911 C -1051.2276,602.95351 -1049.7723,600.14902 -1048.1412,596.92861 C -1048.9386,596.61185 -1049.7389,596.5703 -1050.5532,596.78147 C -1051.3756,596.99266 -1052.3594,597.95412 -1053.0013,598.20696 C -1053.6348,598.44592 -1054.0888,598.45432 -1054.2917,598.24314 C -1054.4806,598.02363 -1054.5035,597.44543 -1054.1229,596.92861 C -1053.7201,596.40067 -1052.9063,595.5533 -1051.9811,595.1365 C -1051.0501,594.71693 -1049.6111,594.46093 -1048.6163,594.44426 C -1048.5538,594.44305 -1048.4906,594.44309 -1048.4282,594.44426 z" />
   </g>
   <g
-     transform="translate(1480.1847,-289.94418)"
+     transform="translate(1480.1847,-369.94418)"
      id="g16552">
     <g
        id="g16340">
@@ -83140,7 +83270,7 @@
   </g>
   <g
      id="g16346"
-     transform="translate(1600.9892,-350.1329)">
+     transform="translate(1600.9892,-430.1329)">
     <rect
        width="24"
        height="24"
@@ -83150,7 +83280,7 @@
        style="opacity:1;fill:#000000;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
   </g>
   <flowRoot
-     transform="matrix(1.6473499,0,0,1.6473499,680.92343,263.57576)"
+     transform="matrix(1.6473499,0,0,1.6473499,680.92343,183.57576)"
      id="flowRoot29856"
      xml:space="preserve"
      style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:url(#linearGradient19976);fill-opacity:1;stroke:#547c1b;stroke-width:0.1061436;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;font-family:Andale Mono"><flowRegion
@@ -83164,7 +83294,7 @@
          style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:url(#linearGradient34167);fill-opacity:1;stroke:#547c1b;stroke-width:0.1061436;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;font-family:Andale Mono" /></flowRegion><flowPara
        id="flowPara29862"
        style="fill:url(#linearGradient19974);fill-opacity:1;stroke:#547c1b;stroke-width:0.1061436;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1">ST</flowPara></flowRoot>  <g
-     transform="matrix(7.1599763e-2,0,0,7.1599763e-2,543.18029,275.95335)"
+     transform="matrix(7.1599763e-2,0,0,7.1599763e-2,543.18029,195.95335)"
      id="g2248">
     <path
        d="M 144.80549,88.557517 C 144.80549,127.69251 113.04391,159.45419 73.909089,159.45419 C 34.773922,159.45419 3.0123414,127.69251 3.0123414,88.557517 C 3.0123414,49.42256 34.773922,17.660874 73.909089,17.660874 C 113.04391,17.660874 144.80549,49.42256 144.80549,88.557517 z"
@@ -83237,7 +83367,7 @@
        style="opacity:0.84418604;fill:#6d9d37;fill-opacity:1;stroke:none" />
   </g>
   <g
-     transform="translate(1660.8886,-380.22727)"
+     transform="translate(1660.8886,-460.22727)"
      id="g16694">
     <g
        id="g16349">
@@ -83332,7 +83462,7 @@
      id="g1161"
      transform="matrix(0.5724346,-0.3079575,0.3079575,0.5724346,131.42904,887.47867)" />
   <g
-     transform="translate(1360.788,-229.831)"
+     transform="translate(1360.788,-309.831)"
      id="g16313">
     <rect
        width="24"
@@ -83408,7 +83538,7 @@
     </g>
   </g>
   <g
-     transform="translate(1420.788,-259.84989)"
+     transform="translate(1420.788,-339.84989)"
      id="g16328">
     <rect
        width="24"
@@ -83464,7 +83594,7 @@
     </g>
   </g>
   <g
-     transform="translate(1121,-109.38324)"
+     transform="translate(1121,-189.38324)"
      id="g16199">
     <rect
        width="24"
@@ -83522,7 +83652,7 @@
   <rect
      style="fill:none;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
      id="HMIEditor"
-     y="271.36218"
+     y="191.36218"
      x="240"
      height="24"
      width="24" />
@@ -83531,9 +83661,9 @@
      style="font-size:12.76000023px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans"
      xml:space="preserve"
      id="text34203"
-     y="544.36218"
+     y="364.36218"
      x="20"><tspan
-       y="544.36218"
+       y="364.36218"
        x="20"
        id="tspan16197"
        sodipodi:role="line">%% Add Delete Disabled Enabled HideVars IECCDown IECCUp Maximize Minimize minus plus ShowVars %%</tspan></text>
@@ -83561,7 +83691,7 @@
      transform="matrix(4.5011397,0,0,4.5011397,2971.834,-119.97324)" />
   <g
      id="g18981"
-     transform="translate(25.999952,123.97794)">
+     transform="translate(25.999952,-56.02206)">
     <rect
        width="15.999955"
        height="15.999955"
@@ -83604,7 +83734,7 @@
      transform="matrix(3.9071406,0,0,3.9071406,2402.8076,-50.595777)" />
   <g
      id="g18994"
-     transform="translate(19.498009,119.61597)">
+     transform="translate(19.498009,-60.38403)">
     <rect
        width="16"
        height="16"
@@ -83630,7 +83760,7 @@
      transform="matrix(1.0031449,0,0,1.0031449,685.39009,256.82525)" />
   <g
      id="g19000"
-     transform="matrix(0.9968618,0,0,0.996865,-396.72428,99.913609)">
+     transform="matrix(0.9968618,0,0,0.996865,-396.72428,-80.086391)">
     <rect
        width="16.050318"
        height="16.050318"
@@ -83648,7 +83778,7 @@
      transform="matrix(1.0031449,0,0,1.0031449,660.09588,272.46095)" />
   <g
      id="g19004"
-     transform="matrix(0.9968618,0,0,0.9968618,-339.72428,79.915124)">
+     transform="matrix(0.9968618,0,0,0.9968618,-339.72428,-100.08488)">
     <rect
        width="16.050318"
        height="16.050318"
@@ -83663,7 +83793,7 @@
   </g>
   <g
      id="g19086"
-     transform="matrix(0.965737,0,0,0.965737,-233.99669,79.166717)">
+     transform="matrix(0.965737,0,0,0.965737,-233.99669,-100.83328)">
     <rect
        width="24.075478"
        height="24.075478"
@@ -83938,14 +84068,14 @@
   <rect
      style="fill:#000000;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
      id="IECCUp"
-     y="551.36218"
+     y="371.36218"
      x="380"
      height="16"
      width="16" />
   <g
      style="fill:url(#linearGradient16279);fill-opacity:1;stroke:url(#linearGradient16281)"
      id="g59085"
-     transform="matrix(1,0,0,-1,-6.571463,804.20104)">
+     transform="matrix(1,0,0,-1,-6.571463,624.20104)">
     <path
        style="fill:url(#linearGradient16274);fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient16276);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:10.43299961;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
        id="path2160"
@@ -83954,14 +84084,14 @@
   <rect
      style="fill:#000000;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
      id="IECCDown"
-     y="551.36218"
+     y="371.36218"
      x="334"
      height="16"
      width="16" />
   <g
      style="fill:url(#linearGradient16270);fill-opacity:1;stroke:url(#linearGradient16272)"
      id="g59118"
-     transform="translate(39.428537,314.5234)">
+     transform="translate(39.428537,134.5234)">
     <path
        style="fill:url(#linearGradient16266);fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient16268);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:10.43299961;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
        id="path59120"
@@ -83969,7 +84099,7 @@
   </g>
   <g
      id="g19018"
-     transform="matrix(0.9968629,0,0,0.9968629,-119.72484,1.91483)">
+     transform="matrix(0.9968629,0,0,0.9968629,-119.72484,-178.08517)">
     <rect
        width="24.075478"
        height="24.075478"
@@ -84175,7 +84305,7 @@
   </g>
   <g
      id="g19359"
-     transform="translate(20,119.91553)">
+     transform="translate(20,-60.08447)">
     <rect
        width="23.999916"
        height="23.999916"
@@ -84307,7 +84437,7 @@
   </g>
   <g
      id="g19146"
-     transform="matrix(0.996861,0,0,0.996861,17.276127,-43.08392)">
+     transform="matrix(0.996861,0,0,0.996861,17.276127,-223.08392)">
     <rect
        width="13.040884"
        height="13.040884"
@@ -84336,7 +84466,7 @@
   </g>
   <g
      id="g19152"
-     transform="matrix(0.996861,0,0,0.996861,58.276127,-63.08387)">
+     transform="matrix(0.996861,0,0,0.996861,58.276127,-243.08387)">
     <rect
        width="13.040884"
        height="13.040884"
@@ -84375,29 +84505,29 @@
      style="font-size:12.76000023px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans"
      xml:space="preserve"
      id="text60407"
-     y="477.44528"
-     x="310.27524"><tspan
-       y="477.44528"
-       x="310.27524"
+     y="257.44528"
+     x="170.27524"><tspan
+       y="257.44528"
+       x="170.27524"
        id="tspan16195"
        sodipodi:role="line">%% Compiler TargetType %%</tspan></text>
   <rect
      style="fill:#000000;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
      id="Compiler"
-     y="487.36218"
-     x="360"
+     y="267.36218"
+     x="220"
      height="24"
      width="24" />
   <rect
      style="fill:#000000;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
      id="TargetType"
-     y="488.43768"
-     x="430"
+     y="268.43768"
+     x="290"
      height="23.999981"
      width="23.999981" />
   <g
      id="g23927"
-     transform="matrix(9.6211589e-2,0,0,9.6211589e-2,359.72267,486.8136)">
+     transform="matrix(9.6211589e-2,0,0,9.6211589e-2,219.72267,266.8136)">
     <g
        id="g23929"
        transform="translate(48.379983,78.100302)" />
@@ -84440,7 +84570,7 @@
   </g>
   <g
      id="g19199"
-     transform="matrix(5.3097304e-2,0,0,5.3097304e-2,387.38564,480.36282)">
+     transform="matrix(5.3097304e-2,0,0,5.3097304e-2,247.38564,260.36282)">
     <circle
        sodipodi:ry="226"
        sodipodi:rx="226"
@@ -84501,44 +84631,44 @@
      style="font-size:20px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
      xml:space="preserve"
      id="text18377"
-     y="154.61037"
+     y="134.61037"
      x="42.860386"><tspan
        id="tspan18379"
-       y="154.61037"
+       y="134.61037"
        x="42.860386">Plugin</tspan><tspan
        id="tspan18381"
-       y="179.61037"
+       y="159.61037"
        x="42.860386">Methods</tspan></text>
   <text
      style="font-size:20px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
      xml:space="preserve"
      id="text18383"
-     y="154.61037"
-     x="283.48929"><tspan
+     y="254.61035"
+     x="43.489288"><tspan
        id="tspan18385"
-       y="154.61037"
-       x="283.48929">Plugin</tspan><tspan
+       y="254.61035"
+       x="43.489288">Plugin</tspan><tspan
        id="tspan18387"
-       y="179.61037"
-       x="283.48929">Params</tspan></text>
+       y="279.61035"
+       x="43.489288">Params</tspan></text>
   <text
      style="font-size:20px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
      xml:space="preserve"
      id="text18389"
-     y="154.61037"
-     x="504.30698"><tspan
+     y="333.61218"
+     x="37.5"><tspan
        id="tspan18393"
-       y="154.61037"
-       x="504.30698">Buttons</tspan></text>
+       y="333.61218"
+       x="37.5">Buttons</tspan></text>
   <text
      style="font-size:40.12579727px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
      xml:space="preserve"
      id="text18397"
-     y="74.610374"
+     y="54.610374"
      x="371.85562"><tspan
        style="text-align:center;text-anchor:middle"
        id="tspan18399"
-       y="74.610374"
+       y="54.610374"
        x="371.85562">Beremiz icons</tspan></text>
   <g
      id="g18993"
@@ -84630,19 +84760,19 @@
          x="-988.61249">Pre-Alpha Release. Copyright © LOLITECH 2008</tspan></text>
   </g>
   <text
-     style="font-size:12.76000023px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+     style="font-size:51.04000092px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
      xml:space="preserve"
      id="text18989"
-     y="771.29181"
-     x="323.28137"><tspan
+     y="761.85242"
+     x="176.98375"><tspan
        id="tspan18991"
-       y="771.29181"
-       x="323.28137"
-       style="font-size:12.76000023px">%% splash %%</tspan></text>
+       y="761.85242"
+       x="176.98375"
+       style="font-size:51.04000092px">%% splash %%</tspan></text>
   <g
      style="display:inline"
      id="g19354"
-     transform="matrix(0.2686638,0,0,0.2686638,754.93573,100.70118)">
+     transform="matrix(0.2686638,0,0,0.2686638,514.93573,-19.29882)">
     <g
        id="g19356"
        mask="url(#mask6467)"
@@ -84748,13 +84878,13 @@
      style="font-size:13.88476658px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
      xml:space="preserve"
      id="text19983"
-     y="629.33417"
-     x="266.00839"><tspan
+     y="509.33417"
+     x="26.008392"><tspan
        id="tspan19985"
-       y="629.33417"
-       x="266.00839">%% ico48 ico24 ico16 %%</tspan></text>
+       y="509.33417"
+       x="26.008392">%% ico48 ico24 ico16 %%</tspan></text>
   <g
-     transform="translate(1084.009,-113.72536)"
+     transform="translate(1084.009,-193.72536)"
      id="g17590">
     <rect
        style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:10.43299961;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
@@ -84835,7 +84965,7 @@
        style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:10.43299961;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
   </g>
   <g
-     transform="translate(1083.788,-113.94633)"
+     transform="translate(1083.788,-193.94633)"
      style="fill:#4fadf7;fill-opacity:1"
      id="g17603">
     <rect
@@ -84917,7 +85047,7 @@
        y="404.23816" />
   </g>
   <path
-     transform="translate(-0.212,-7.035e-2)"
+     transform="translate(-0.212,-80.07035)"
      d="M 263.65515,289.22899 L 258.6897,286.36218 L 263.65515,283.49538 L 263.65515,289.22899 z"
      inkscape:randomized="0"
      inkscape:rounded="0"
@@ -84947,9 +85077,9 @@
      inkscape:rounded="0"
      inkscape:randomized="0"
      d="M 263.65515,289.22899 L 258.6897,286.36218 L 263.65515,283.49538 L 263.65515,289.22899 z"
-     transform="translate(-0.5214,-0.3797)" />
+     transform="translate(-0.5214,-80.3797)" />
   <g
-     transform="matrix(8.8340245e-2,0,0,8.8340245e-2,320.46956,254.13123)"
+     transform="matrix(8.8340245e-2,0,0,8.8340245e-2,320.46956,174.13123)"
      id="g17968">
     <g
        style="display:inline"
@@ -85051,27 +85181,27 @@
        transform="matrix(0.5324675,0,0,0.5324675,-889.75288,329.57107)" />
   </g>
   <text
-     x="33.295933"
-     y="341.52582"
+     x="113.29593"
+     y="121.52582"
      id="text16266"
      xml:space="preserve"
      style="font-size:12.76095104px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans"
      sodipodi:linespacing="125%"><tspan
        sodipodi:role="line"
        id="tspan16268"
-       x="33.295933"
-       y="341.52582">%% editIECrawcode EditCfile %%</tspan></text>
+       x="113.29593"
+       y="121.52582">%% editIECrawcode EditCfile Transfer Connect Disconnect Debug %%</tspan></text>
   <rect
      width="24"
      height="24"
-     x="60"
-     y="371.36218"
+     x="140"
+     y="131.36218"
      id="editIECrawcode"
      style="fill:#000000;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
      inkscape:label="#rect16270" />
   <g
      id="g20019"
-     transform="translate(-600.13257,100)">
+     transform="translate(-520.13257,-140)">
     <flowRoot
        transform="matrix(1.6473499,0,0,1.6473499,800.92342,263.57576)"
        id="flowRoot19870"
@@ -85283,12 +85413,12 @@
      inkscape:label="#rect16270"
      style="fill:#000000;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
      id="EditCfile"
-     y="371.36218"
-     x="180"
+     y="131.36218"
+     x="260"
      height="24"
      width="24" />
   <g
-     transform="translate(-480.13257,100)"
+     transform="translate(-400.13257,-140)"
      id="g20864">
     <flowRoot
        style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:url(#linearGradient20956);fill-opacity:1;stroke:#547c1b;stroke-width:0.1061436;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;font-family:Andale Mono"
@@ -85497,4 +85627,81 @@
        d="M 683.74504,273.49232 C 682.52118,272.4238 682.05427,273.42101 681.27718,274.09784 C 681.11122,273.95433 680.94525,273.81082 680.77929,273.66732 C 681.36055,272.97034 681.65306,272.60698 682.5725,272.17027 C 682.75266,272.27845 683.47211,273.29694 683.74504,273.49232 z"
        style="fill:url(#linearGradient20979);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.25pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
   </g>
+  <text
+     x="37.5"
+     y="473.61218"
+     id="text16382"
+     xml:space="preserve"
+     style="font-size:20px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"><tspan
+       x="37.5"
+       y="473.61218"
+       id="tspan16384">Icons</tspan></text>
+  <rect
+     width="24"
+     height="24"
+     x="320"
+     y="131.36218"
+     id="Transfer"
+     style="fill:#000000;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+     inkscape:label="#rect16270" />
+  <rect
+     inkscape:label="#rect16270"
+     style="fill:#000000;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+     id="Connect"
+     y="131.36218"
+     x="380"
+     height="24"
+     width="24" />
+  <rect
+     width="24"
+     height="24"
+     x="440"
+     y="131.36218"
+     id="Disconnect"
+     style="fill:#000000;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+     inkscape:label="#rect16270" />
+  <path
+     style="opacity:1;fill:url(#linearGradient17534);fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:8.59499931;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+     d="M 392,137.40625 C 388.95578,137.61016 386.40951,139.60837 385.4375,142.375 L 380,142.375 L 380,147.375 L 385.4375,147.375 C 386.41696,150.12787 388.96436,152.1385 392,152.34375 L 392,137.40625 z M 393,137.40625 L 393,152.34375 C 396.03564,152.1385 398.58304,150.12787 399.5625,147.375 L 404,147.375 L 404,142.375 L 399.5625,142.375 C 398.59049,139.60837 396.04422,137.61016 393,137.40625 z"
+     id="path16742" />
+  <path
+     style="opacity:1;fill:url(#linearGradient17532);fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:8.59499931;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+     d="M 448,137.40625 C 444.95578,137.61016 442.40951,139.60837 441.4375,142.375 L 436,142.375 L 436,147.375 L 441.4375,147.375 C 442.41696,150.12787 444.96436,152.1385 448,152.34375 L 448,137.40625 z"
+     id="path16754" />
+  <path
+     style="opacity:1;fill:url(#linearGradient17536);fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:8.59499931;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+     d="M 456,137.42468 L 456,152.36218 C 459.03564,152.15693 461.58304,150.1463 462.5625,147.39343 L 467,147.39343 L 467,142.39343 L 462.5625,142.39343 C 461.59049,139.6268 459.04422,137.62859 456,137.42468 z"
+     id="path16750" />
+  <path
+     id="path17538"
+     d="M 332,137.40625 C 328.95578,137.61016 326.40951,139.60837 325.4375,142.375 L 320,142.375 L 320,147.375 L 325.4375,147.375 C 326.41696,150.12787 328.96436,152.1385 332,152.34375 L 332,137.40625 z M 333,137.40625 L 333,152.34375 C 336.03564,152.1385 338.58304,150.12787 339.5625,147.375 L 344,147.375 L 344,142.375 L 339.5625,142.375 C 338.59049,139.60837 336.04422,137.61016 333,137.40625 z"
+     style="opacity:1;fill:url(#linearGradient17540);fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:8.59499931;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+  <path
+     sodipodi:type="star"
+     style="opacity:1;fill:url(#linearGradient17552);fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:8.59499931;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+     id="path17544"
+     sodipodi:sides="3"
+     sodipodi:cx="332"
+     sodipodi:cy="144.36218"
+     sodipodi:r1="5.6378174"
+     sodipodi:r2="2.8189087"
+     sodipodi:arg1="1.5707963"
+     sodipodi:arg2="2.6179939"
+     inkscape:flatsided="true"
+     inkscape:rounded="0"
+     inkscape:randomized="0"
+     d="M 332,150 L 327.11751,141.54327 L 336.88249,141.54327 L 332,150 z"
+     transform="matrix(1.1031299,0.6368924,-0.6368924,1.1031299,58.022874,-226.14748)" />
+  <rect
+     inkscape:label="#rect16270"
+     style="fill:#000000;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+     id="Debug"
+     y="131.36218"
+     x="500"
+     height="24"
+     width="24" />
+  <path
+     style="opacity:1;fill:#160379;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+     d="M 514.14211,134.18201 L 513.44846,134.37119 L 514.09481,136.75167 C 513.55664,136.88976 513.034,137.15037 512.61293,137.57144 C 512.58031,137.60404 512.56503,137.64813 512.53409,137.68179 C 511.78169,137.61864 511.00589,137.70771 510.24821,137.94979 L 509.60186,135.53777 L 508.90819,135.72696 L 509.5861,138.20203 C 508.90904,138.50465 508.25523,138.92751 507.67856,139.47899 L 505.51876,138.13898 L 505.15618,138.7538 L 507.18983,139.99922 C 506.46851,140.85118 505.98679,141.84216 505.771,142.83687 L 503.56394,142.23781 L 503.37475,142.93146 L 505.67641,143.56206 C 505.54986,144.9582 505.92466,146.33767 506.82724,147.39291 L 514.37857,139.84157 L 514.88305,140.34605 L 507.33172,147.89738 C 508.2984,148.72357 509.54404,149.1165 510.81574,149.07974 L 511.28869,150.84539 L 511.98234,150.65621 L 511.54092,149.01668 C 512.59832,148.85042 513.66579,148.38127 514.58351,147.64515 L 515.46634,149.06397 L 516.08117,148.70138 L 515.13529,147.15642 C 515.82639,146.46532 516.32697,145.65819 516.64871,144.82323 L 518.49319,145.31195 L 518.68237,144.6183 L 516.86941,144.1296 C 517.03506,143.48333 517.09629,142.82751 517.04282,142.19052 C 517.07648,142.15959 517.12057,142.14431 517.15319,142.11171 C 517.57426,141.69063 517.83486,141.16798 517.97296,140.6298 L 520.35344,141.27617 L 520.54262,140.58251 L 518.08331,139.92038 C 518.10481,139.07152 517.80055,138.21881 517.15319,137.57144 C 516.50581,136.92407 515.6531,136.61982 514.80424,136.64131 L 514.14211,134.18201 z"
+     id="path16411" />
 </svg>
--- a/plugger.py	Tue Aug 12 16:27:07 2008 +0200
+++ b/plugger.py	Wed Aug 20 00:11:40 2008 +0200
@@ -70,6 +70,10 @@
     def BufferProject(self):
         pass
 
+# helper func to get path to images
+def opjimg(imgname):
+    return os.path.join("images",imgname)
+
 class PlugTemplate:
     """
     This class is the one that define plugins.
@@ -100,9 +104,6 @@
         # copy PluginMethods so that it can be later customized
         self.PluginMethods = [dic.copy() for dic in self.PluginMethods]
 
-    def IsGUIPlugin(self):
-        return False
-
     def PluginBaseXmlFilePath(self, PlugName=None):
         return os.path.join(self.PlugPath(PlugName), "baseplugin.xml")
     
@@ -112,7 +113,8 @@
     def PlugPath(self,PlugName=None):
         if not PlugName:
             PlugName = self.BaseParams.getName()
-        return os.path.join(self.PlugParent.PlugPath(), PlugName + NameTypeSeparator + self.PlugType)
+        return os.path.join(self.PlugParent.PlugPath(),
+                            PlugName + NameTypeSeparator + self.PlugType)
     
     def PlugTestModified(self):
         return self.ChangesToSave
@@ -149,13 +151,13 @@
                 params.append(self.PlugParams[1].getElementInfos(self.PlugParams[0]))
             return params
         
-    def SetParamsAttribute(self, path, value, logger):
+    def SetParamsAttribute(self, path, value):
         self.ChangesToSave = True
         # Filter IEC_Channel and Name, that have specific behavior
         if path == "BaseParams.IEC_Channel":
-            return self.FindNewIEC_Channel(value,logger), True
+            return self.FindNewIEC_Channel(value), True
         elif path == "BaseParams.Name":
-            res = self.FindNewName(value,logger)
+            res = self.FindNewName(value)
             self.PlugRequestSave()
             return res, True
         
@@ -205,7 +207,7 @@
         shutil.copytree(src_PlugPath, self.PlugPath)
         return True
 
-    def PlugGenerate_C(self, buildpath, locations, logger):
+    def PlugGenerate_C(self, buildpath, locations):
         """
         Generate C code
         @param locations: List of complete variables locations \
@@ -217,12 +219,15 @@
             }, ...]
         @return: [(C_file_name, CFLAGS),...] , LDFLAGS_TO_APPEND
         """
-        logger.write_warning(".".join(map(lambda x:str(x), self.GetCurrentLocation())) + " -> Nothing to do\n")
+        self.logger.write_warning(".".join(map(lambda x:str(x), self.GetCurrentLocation())) + " -> Nothing to do\n")
         return [],"",False
     
-    def _Generate_C(self, buildpath, locations, logger):
-        # Generate plugins [(Cfiles, CFLAGS)], LDFLAGS
-        PlugCFilesAndCFLAGS, PlugLDFLAGS, DoCalls = self.PlugGenerate_C(buildpath, locations, logger)
+    def _Generate_C(self, buildpath, locations):
+        # Generate plugins [(Cfiles, CFLAGS)], LDFLAGS, DoCalls, extra_files
+        # extra_files = [(fname,fobject), ...]
+        gen_result = self.PlugGenerate_C(buildpath, locations)
+        PlugCFilesAndCFLAGS, PlugLDFLAGS, DoCalls = gen_result[:3]
+        extra_files = gen_result[3:]
         # if some files heve been generated put them in the list with their location
         if PlugCFilesAndCFLAGS:
             LocationCFilesAndCFLAGS = [(self.GetCurrentLocation(), PlugCFilesAndCFLAGS, DoCalls)]
@@ -245,19 +250,18 @@
             new_location = PlugChild.GetCurrentLocation()
             # How deep are we in the tree ?
             depth=len(new_location)
-            _LocationCFilesAndCFLAGS, _LDFLAGS = \
+            _LocationCFilesAndCFLAGS, _LDFLAGS, _extra_files = \
                 PlugChild._Generate_C(
                     #keep the same path
                     buildpath,
                     # filter locations that start with current IEC location
-                    [loc for loc in locations if loc["LOC"][0:depth] == new_location ],
-                    #propagete logger
-                    logger)
+                    [loc for loc in locations if loc["LOC"][0:depth] == new_location ])
             # stack the result
             LocationCFilesAndCFLAGS += _LocationCFilesAndCFLAGS
             LDFLAGS += _LDFLAGS
-        
-        return LocationCFilesAndCFLAGS,LDFLAGS
+            extra_files += _extra_files
+        
+        return LocationCFilesAndCFLAGS, LDFLAGS, extra_files
 
     def BlockTypesFactory(self):
         return []
@@ -343,7 +347,7 @@
         else:
             return {"name" : self.BaseParams.getName(), "channel" : self.BaseParams.getIEC_Channel(), "enabled" : self.BaseParams.getEnabled(), "parent" : len(self.PlugChildsTypes) > 0, "type" : self.BaseParams.getName(), "values" : childs}
     
-    def FindNewName(self, DesiredName, logger):
+    def FindNewName(self, DesiredName):
         """
         Changes Name to DesiredName if available, Name-N if not.
         @param DesiredName: The desired Name (string)
@@ -376,10 +380,10 @@
             shutil.move(oldname, self.PlugPath())
         # warn user he has two left hands
         if DesiredName != res:
-            logger.write_warning("A child names \"%s\" already exist -> \"%s\"\n"%(DesiredName,res))
+            self.logger.write_warning("A child names \"%s\" already exist -> \"%s\"\n"%(DesiredName,res))
         return res
 
-    def FindNewIEC_Channel(self, DesiredChannel, logger):
+    def FindNewIEC_Channel(self, DesiredChannel):
         """
         Changes IEC Channel number to DesiredChannel if available, nearest available if not.
         @param DesiredChannel: The desired IEC channel (int)
@@ -401,15 +405,14 @@
             if res < CurrentChannel: # Want to go down ?
                 res -=  1 # Test for n-1
                 if res < 0 :
-                    if logger :
-                        logger.write_warning("Cannot find lower free IEC channel than %d\n"%CurrentChannel)
+                    self.logger.write_warning("Cannot find lower free IEC channel than %d\n"%CurrentChannel)
                     return CurrentChannel # Can't go bellow 0, do nothing
             else : # Want to go up ?
                 res +=  1 # Test for n-1
         # Finally set IEC Channel
         self.BaseParams.setIEC_Channel(res)
-        if logger and DesiredChannel != res:
-            logger.write_warning("A child with IEC channel %d already exist -> %d\n"%(DesiredChannel,res))
+        if DesiredChannel != res:
+            self.logger.write_warning("A child with IEC channel %d already exist -> %d\n"%(DesiredChannel,res))
         return res
 
     def OnPlugClose(self):
@@ -433,7 +436,7 @@
         # Ask to his parent to remove it
         self.PlugParent._doRemoveChild(self)
 
-    def PlugAddChild(self, PlugName, PlugType, logger):
+    def PlugAddChild(self, PlugName, PlugType):
         """
         Create the plugins that may be added as child to this node self
         @param PlugType: string desining the plugin class name (get name from PlugChildsTypes)
@@ -469,6 +472,8 @@
             def __init__(_self):
                 # self is the parent
                 _self.PlugParent = self
+                # self is the parent
+                _self.logger = self.logger
                 # Keep track of the plugin type name
                 _self.PlugType = PlugType
                 # remind the help string, for more fancy display
@@ -476,11 +481,11 @@
                 # Call the base plugin template init - change XSD into class members
                 PlugTemplate.__init__(_self)
                 # check name is unique
-                NewPlugName = _self.FindNewName(PlugName, logger)
+                NewPlugName = _self.FindNewName(PlugName)
                 # If dir have already be made, and file exist
                 if os.path.isdir(_self.PlugPath(NewPlugName)): #and os.path.isfile(_self.PluginXmlFilePath(PlugName)):
                     #Load the plugin.xml file into parameters members
-                    _self.LoadXMLParams(logger, NewPlugName)
+                    _self.LoadXMLParams(NewPlugName)
                     # Basic check. Better to fail immediately.
                     if (_self.BaseParams.getName() != NewPlugName):
                         raise Exception, "Project tree layout do not match plugin.xml %s!=%s "%(NewPlugName, _self.BaseParams.getName())
@@ -488,12 +493,12 @@
                     # Now, self.PlugPath() should be OK
                     
                     # Check that IEC_Channel is not already in use.
-                    _self.FindNewIEC_Channel(_self.BaseParams.getIEC_Channel(),logger)
+                    _self.FindNewIEC_Channel(_self.BaseParams.getIEC_Channel())
                     # Call the plugin real __init__
                     if getattr(PlugClass, "__init__", None):
                         PlugClass.__init__(_self)
                     #Load and init all the childs
-                    _self.LoadChilds(logger)
+                    _self.LoadChilds()
                     #just loaded, nothing to saved
                     _self.ChangesToSave = False
                 else:
@@ -519,42 +524,44 @@
         return newPluginOpj
             
 
-    def LoadXMLParams(self, logger, PlugName = None):
+    def LoadXMLParams(self, PlugName = None):
         methode_name = os.path.join(self.PlugPath(PlugName), "methods.py")
         if os.path.isfile(methode_name):
             execfile(methode_name)
 
         # Get the base xml tree
         if self.MandatoryParams:
-            #try:
+            try:
                 basexmlfile = open(self.PluginBaseXmlFilePath(PlugName), 'r')
                 basetree = minidom.parse(basexmlfile)
                 self.MandatoryParams[1].loadXMLTree(basetree.childNodes[0])
                 basexmlfile.close()
-            #except Exception, e:
-            #    logger.write_error("Couldn't load plugin base parameters %s :\n %s" % (PlugName, str(e)))
-                
+            except Exception, exc:
+                self.logger.write_error("Couldn't load plugin base parameters %s :\n %s" % (PlugName, str(exc)))
+                self.logger.write_error(traceback.format_exc())
         
         # Get the xml tree
         if self.PlugParams:
-            #try:
+            try:
                 xmlfile = open(self.PluginXmlFilePath(PlugName), 'r')
                 tree = minidom.parse(xmlfile)
                 self.PlugParams[1].loadXMLTree(tree.childNodes[0])
                 xmlfile.close()
-            #except Exception, e:
-            #    logger.write_error("Couldn't load plugin parameters %s :\n %s" % (PlugName, str(e)))
-        
-    def LoadChilds(self, logger):
+            except Exception, exc:
+                self.logger.write_error("Couldn't load plugin parameters %s :\n %s" % (PlugName, str(exc)))
+                self.logger.write_error(traceback.format_exc())
+        
+    def LoadChilds(self):
         # Iterate over all PlugName@PlugType in plugin directory, and try to open them
         for PlugDir in os.listdir(self.PlugPath()):
             if os.path.isdir(os.path.join(self.PlugPath(), PlugDir)) and \
                PlugDir.count(NameTypeSeparator) == 1:
                 pname, ptype = PlugDir.split(NameTypeSeparator)
-                #try:
-                self.PlugAddChild(pname, ptype, logger)
-                #except Exception, e:
-                #    logger.write_error("Could not add child \"%s\", type %s :\n%s\n"%(pname, ptype, str(e)))
+                try:
+                    self.PlugAddChild(pname, ptype)
+                except Exception, exc:
+                    self.logger.write_error("Could not add child \"%s\", type %s :\n%s\n"%(pname, ptype, str(exc)))
+                    self.logger.write_error(traceback.format_exc())
 
     def EnableMethod(self, method, value):
         for d in self.PluginMethods:
@@ -563,6 +570,13 @@
                 return True
         return False
 
+    def ShowMethod(self, method, value):
+        for d in self.PluginMethods:
+            if d["method"]==method:
+                d["shown"]=value
+                return True
+        return False
+
 def _GetClassFunction(name):
     def GetRootClass():
         return getattr(__import__("plugins." + name), name).RootClass
@@ -586,15 +600,24 @@
 ieclib_path = os.path.join(base_folder, "matiec", "lib")
 
 # import for project creation timestamping
+from threading import Timer
 from time import localtime
 from datetime import datetime
 # import necessary stuff from PLCOpenEditor
 from PLCControler import PLCControler
 from PLCOpenEditor import PLCOpenEditor, ProjectDialog
 from TextViewer import TextViewer
-from plcopen.structures import IEC_KEYWORDS
+from plcopen.structures import IEC_KEYWORDS, TypeHierarchy_list
+
+# Construct debugger natively supported types
+DebugTypes = [t for t in zip(*TypeHierarchy_list)[0] if not t.startswith("ANY")] + \
+    ["STEP","TRANSITION","ACTION"]
+
 import runtime
 import re
+import targets
+import connectors
+from discovery import DiscoveryDialog
 
 class PluginsRoot(PlugTemplate, PLCControler):
     """
@@ -619,77 +642,36 @@
             <xsd:element name="TargetType">
               <xsd:complexType>
                 <xsd:choice>
-                  <xsd:element name="Win32">
-                    <xsd:complexType>
-                      <xsd:attribute name="Priority" type="xsd:integer" use="required"/>
-                    </xsd:complexType>
-                  </xsd:element>
-                  <xsd:element name="Linux">
-                    <xsd:complexType>
-                      <xsd:attribute name="Nice" type="xsd:integer" use="required"/>
-                    </xsd:complexType>
-                  </xsd:element>
-                  <xsd:element name="Xenomai">
-                    <xsd:complexType>
-                      <xsd:attribute name="xeno_config" type="xsd:string" use="optional" default="/usr/xenomai/"/>
-                      <xsd:attribute name="Priority" type="xsd:integer" use="required"/>
-                    </xsd:complexType>
-                  </xsd:element>
-                  <xsd:element name="RTAI">
-                    <xsd:complexType>
-                      <xsd:attribute name="rtai_config" type="xsd:string" use="required"/>
-                      <xsd:attribute name="Priority" type="xsd:integer" use="required"/>
-                    </xsd:complexType>
-                  </xsd:element>
-                  <xsd:element name="Library">
-                    <xsd:complexType>
-                      <xsd:attribute name="Dynamic" type="xsd:boolean" use="optional" default="true"/>
-                    </xsd:complexType>
-                  </xsd:element>
-                </xsd:choice>
-              </xsd:complexType>
-            </xsd:element>
-            <xsd:element name="Connection">
-              <xsd:complexType>
-                <xsd:choice>
-                  <xsd:element name="Local"/>
-                  <xsd:element name="TCP_IP">
-                    <xsd:complexType>
-                      <xsd:attribute name="Host" type="xsd:string" use="required"/>
-                    </xsd:complexType>
-                  </xsd:element>
+                """+targets.targetchoices+"""
                 </xsd:choice>
               </xsd:complexType>
             </xsd:element>
           </xsd:sequence>
-          <xsd:attribute name="Compiler" type="xsd:string" use="optional" default="gcc"/>
-          <xsd:attribute name="CFLAGS" type="xsd:string" use="required"/>
-          <xsd:attribute name="Linker" type="xsd:string" use="optional" default="ld"/>
-          <xsd:attribute name="LDFLAGS" type="xsd:string" use="required"/>
-          <xsd:attribute name="Sync_Align_Ratio" use="optional" default="50">
-            <xsd:simpleType>
-                <xsd:restriction base="xsd:integer">
-                    <xsd:minInclusive value="1"/>
-                    <xsd:maxInclusive value="99"/>
-                </xsd:restriction>
-            </xsd:simpleType>
-          </xsd:attribute>
         </xsd:complexType>
       </xsd:element>
     </xsd:schema>
     """
 
-    def __init__(self, frame):
+    def __init__(self, frame, logger):
         PLCControler.__init__(self)
         
         self.MandatoryParams = None
         self.AppFrame = frame
-        
-        """
-        This method are not called here... but in NewProject and OpenProject
-        self._AddParamsMembers()
-        self.PluggedChilds = {}
-        """
+        self.logger = logger
+        self._builder = None
+        self._connector = None
+        
+        # Setup debug information
+        self.IECdebug_callables = {}
+        # Timer to prevent rapid-fire when registering many variables
+        self.DebugTimer=Timer(0.5,self.RegisterDebugVarToConnector)
+        self.ResetIECProgramsAndVariables()
+
+        
+        #This method are not called here... but in NewProject and OpenProject
+        #self._AddParamsMembers()
+        #self.PluggedChilds = {}
+
         # In both new or load scenario, no need to save
         self.ChangesToSave = False        
         # root have no parent
@@ -769,14 +751,14 @@
         self.SaveProject()
         return None
         
-    def LoadProject(self, ProjectPath, logger):
+    def LoadProject(self, ProjectPath):
         """
         Load a project contained in a folder
         @param ProjectPath: path of the project folder
         """
         if os.path.basename(ProjectPath) == "":
             ProjectPath = os.path.dirname(ProjectPath)
-        # Verify that project contains a PLCOpen program
+		# Verify that project contains a PLCOpen program
         plc_file = os.path.join(ProjectPath, "plc.xml")
         if not os.path.isfile(plc_file):
             return "Folder choosen doesn't contain a program. It's not a valid project!"
@@ -792,12 +774,19 @@
         # If dir have already be made, and file exist
         if os.path.isdir(self.PlugPath()) and os.path.isfile(self.PluginXmlFilePath()):
             #Load the plugin.xml file into parameters members
-            result = self.LoadXMLParams(logger)
+            result = self.LoadXMLParams()
             if result:
                 return result
             #Load and init all the childs
-            self.LoadChilds(logger)
+            self.LoadChilds()
         self.RefreshPluginsBlockLists()
+        
+        if os.path.exists(self._getBuildPath()):
+            self.EnableMethod("_Clean", True)
+
+        if os.path.isfile(self._getIECrawcodepath()):
+            self.ShowMethod("_showIECcode", True)
+
         return None
     
     def SaveProject(self):
@@ -827,19 +816,12 @@
     def PluginXmlFilePath(self, PlugName=None):
         return os.path.join(self.PlugPath(PlugName), "beremiz.xml")
 
-    def PlugGenerate_C(self, buildpath, locations, logger):
-        """
-        Generate C code
-        @param locations: List of complete variables locations \
-            [(IEC_loc, IEC_Direction, IEC_Type, Name)]\
-            ex: [((0,0,4,5),'I','STRING','__IX_0_0_4_5'),...]
-        @return: [(C_file_name, CFLAGS),...] , LDFLAGS_TO_APPEND
-        """
-        return [(C_file_name, self.CFLAGS) for C_file_name in self.PLCGeneratedCFiles ] , "", False
-    
     def _getBuildPath(self):
         return os.path.join(self.ProjectPath, "build")
     
+    def _getExtraFilesPath(self):
+        return os.path.join(self._getBuildPath(), "extra_files")
+
     def _getIECcodepath(self):
         # define name for IEC code file
         return os.path.join(self._getBuildPath(), "plc.st")
@@ -850,7 +832,7 @@
     
     def _getIECrawcodepath(self):
         # define name for IEC raw code file
-        return os.path.join(self._getBuildPath(), "raw_plc.st")
+        return os.path.join(self.PlugPath(), "raw_plc.st")
     
     def GetLocations(self):
         locations = []
@@ -877,23 +859,22 @@
                     locations.append(resdict)
         return locations
         
-    def _Generate_SoftPLC(self, logger):
+    def _Generate_SoftPLC(self):
         """
         Generate SoftPLC ST/IL/SFC code out of PLCOpenEditor controller, and compile it with IEC2C
         @param buildpath: path where files should be created
-        @param logger: the log pseudo file
         """
 
         # Update PLCOpenEditor Plugin Block types before generate ST code
         self.RefreshPluginsBlockLists()
         
-        logger.write("Generating SoftPLC IEC-61131 ST/IL/SFC code...\n")
+        self.logger.write("Generating SoftPLC IEC-61131 ST/IL/SFC code...\n")
         buildpath = self._getBuildPath()
         # ask PLCOpenEditor controller to write ST/IL/SFC code file
         result = self.GenerateProgram(self._getIECgeneratedcodepath())
         if result is not None:
             # Failed !
-            logger.write_error("Error in ST/IL/SFC code generator :\n%s\n"%result)
+            self.logger.write_error("Error in ST/IL/SFC code generator :\n%s\n"%result)
             return False
         plc_file = open(self._getIECcodepath(), "w")
         if os.path.isfile(self._getIECrawcodepath()):
@@ -901,11 +882,11 @@
             plc_file.write("\n")
         plc_file.write(open(self._getIECgeneratedcodepath(), "r").read())
         plc_file.close()
-        logger.write("Compiling IEC Program in to C code...\n")
+        self.logger.write("Compiling IEC Program in to C code...\n")
         # Now compile IEC code into many C files
         # files are listed to stdout, and errors to stderr. 
         status, result, err_result = ProcessLogger(
-               logger,
+               self.logger,
                "\"%s\" -f \"%s\" -I \"%s\" \"%s\""%(
                          iec2c_path,
                          self._getIECcodepath(),
@@ -913,27 +894,238 @@
                no_stdout=True).spin()
         if status:
             # Failed !
-            logger.write_error("Error : IEC to C compiler returned %d\n"%status)
+            self.logger.write_error("Error : IEC to C compiler returned %d\n"%status)
             return False
         # Now extract C files of stdout
         C_files = [ fname for fname in result.splitlines() if fname[-2:]==".c" or fname[-2:]==".C" ]
         # remove those that are not to be compiled because included by others
         C_files.remove("POUS.c")
         if not C_files:
-            logger.write_error("Error : At least one configuration and one ressource must be declared in PLC !\n")
+            self.logger.write_error("Error : At least one configuration and one ressource must be declared in PLC !\n")
             return False
         # transform those base names to full names with path
         C_files = map(lambda filename:os.path.join(buildpath, filename), C_files)
-        logger.write("Extracting Located Variables...\n")
+        self.logger.write("Extracting Located Variables...\n")
         # Keep track of generated located variables for later use by self._Generate_C
         self.PLCGeneratedLocatedVars = self.GetLocations()
         # Keep track of generated C files for later use by self.PlugGenerate_C
         self.PLCGeneratedCFiles = C_files
         # compute CFLAGS for plc
-        self.CFLAGS = "\"-I"+ieclib_path+"\""
+        self.plcCFLAGS = "\"-I"+ieclib_path+"\""
         return True
 
-    def _build(self, logger):
+    def GetBuilder(self):
+        """
+        Return a Builder (compile C code into machine code)
+        """
+        # Get target, module and class name
+        targetname = self.BeremizRoot.getTargetType().getcontent()["name"]
+        modulename = "targets." + targetname
+        classname = targetname + "_target"
+
+        # Get module reference
+        try :
+            targetmodule = getattr(__import__(modulename), targetname)
+
+        except Exception, msg:
+            self.logger.write_error("Can't find module for target %s!\n"%targetname)
+            self.logger.write_error(str(msg))
+            return None
+        
+        # Get target class
+        targetclass = getattr(targetmodule, classname)
+
+        # if target already 
+        if self._builder is None or not isinstance(self._builder,targetclass):
+            # Get classname instance
+            self._builder = targetclass(self)
+        return self._builder
+
+    def GetLastBuildMD5(self):
+        builder=self.GetBuilder()
+        if builder is not None:
+            return builder.GetBinaryCodeMD5()
+        else:
+            return None
+
+    #######################################################################
+    #
+    #                C CODE GENERATION METHODS
+    #
+    #######################################################################
+    
+    def PlugGenerate_C(self, buildpath, locations):
+        """
+        Return C code generated by iec2c compiler 
+        when _generate_softPLC have been called
+        @param locations: ignored
+        @return: [(C_file_name, CFLAGS),...] , LDFLAGS_TO_APPEND
+        """
+        return [(C_file_name, self.plcCFLAGS) for C_file_name in self.PLCGeneratedCFiles ] , "-lrt", False
+    
+    def ResetIECProgramsAndVariables(self):
+        """
+        Reset variable and program list that are parsed from
+        CSV file generated by IEC2C compiler.
+        """
+        self._ProgramList = None
+        self._VariablesList = None
+        self._IECPathToIdx = None
+        self._IdxToIECPath = None
+        
+    def GetIECProgramsAndVariables(self):
+        """
+        Parse CSV-like file  VARIABLES.csv resulting from IEC2C compiler.
+        Each section is marked with a line staring with '//'
+        list of all variables used in various POUs
+        """
+        if self._ProgramList is None or self._VariablesList is None:
+            try:
+                csvfile = os.path.join(self._getBuildPath(),"VARIABLES.csv")
+                # describes CSV columns
+                ProgramsListAttributeName = ["num", "C_path", "type"]
+                VariablesListAttributeName = ["num", "vartype", "IEC_path", "C_path", "type"]
+                self._ProgramList = []
+                self._VariablesList = []
+                self._IECPathToIdx = {}
+                self._IdxToIECPath = {}
+                
+                # Separate sections
+                ListGroup = []
+                for line in open(csvfile,'r').xreadlines():
+                    strippedline = line.strip()
+                    if strippedline.startswith("//"):
+                        # Start new section
+                        ListGroup.append([])
+                    elif len(strippedline) > 0 and len(ListGroup) > 0:
+                        # append to this section
+                        ListGroup[-1].append(strippedline)
+        
+                # first section contains programs
+                for line in ListGroup[0]:
+                    # Split and Maps each field to dictionnary entries
+                    attrs = dict(zip(ProgramsListAttributeName,line.strip().split(';')))
+                    # Truncate "C_path" to remove conf an ressources names
+                    attrs["C_path"] = '__'.join(attrs["C_path"].split(".",2)[1:])
+                    # Push this dictionnary into result.
+                    self._ProgramList.append(attrs)
+        
+                # second section contains all variables
+                for line in ListGroup[1]:
+                    # Split and Maps each field to dictionnary entries
+                    attrs = dict(zip(VariablesListAttributeName,line.strip().split(';')))
+                    # Truncate "C_path" to remove conf an ressources names
+                    attrs["C_path"] = '__'.join(attrs["C_path"].split(".",2)[1:])
+                    # Push this dictionnary into result.
+                    self._VariablesList.append(attrs)
+                    # Fill in IEC<->C translation dicts
+                    IEC_path=attrs["IEC_path"]
+                    Idx=int(attrs["num"])
+                    self._IECPathToIdx[IEC_path]=Idx
+                    self._IdxToIECPath[Idx]=IEC_path
+            except Exception,e:
+                self.logger.write_error("Cannot open/parse VARIABLES.csv!\n")
+                self.logger.write_error(traceback.format_exc())
+                self.ResetIECProgramsAndVariables()
+                return False
+
+        return True
+
+    def Generate_plc_debugger(self):
+        """
+        Generate trace/debug code out of PLC variable list
+        """
+        self.GetIECProgramsAndVariables()
+
+        # prepare debug code
+        debug_code = runtime.code("plc_debug") % {
+           "programs_declarations":
+               "\n".join(["extern %(type)s %(C_path)s;"%p for p in self._ProgramList]),
+           "extern_variables_declarations":"\n".join([
+              {"PT":"extern %(type)s *%(C_path)s;",
+               "VAR":"extern %(type)s %(C_path)s;"}[v["vartype"]]%v 
+               for v in self._VariablesList if v["C_path"].find('.')<0]),
+           "subscription_table_count":
+               len(self._VariablesList),
+           "variables_pointer_type_table_count":
+               len(self._VariablesList),
+           "variables_pointer_type_table_initializer":"\n".join([
+               {"PT":"    variable_table[%(num)s].ptrvalue = (void*)(%(C_path)s);\n",
+                "VAR":"    variable_table[%(num)s].ptrvalue = (void*)(&%(C_path)s);\n"}[v["vartype"]]%v + 
+                "    variable_table[%(num)s].type = %(type)s_ENUM;\n"%v
+                for v in self._VariablesList if v["type"] in DebugTypes ])}
+        
+        return debug_code
+
+    def RegisterDebugVarToConnector(self):
+        Idxs = []
+        if self._connector is not None:
+            for IECPath,WeakCallableDict in self.IECdebug_callables:
+                if len(WeakCallableDict) == 0:
+                    # Callable Dict is empty.
+                    # This variable is not needed anymore!
+                    self.IECdebug_callables.pop(IECPath)
+                else:
+                    # Convert 
+                    Idx = self._IECPathToIdx.get(IECPath,None)
+                    if Idx is not None:
+                        Idxs.append(Idx)
+                    else:
+                        self.logger.write_warning("Debug : Unknown variable %s\n"%IECPath)
+            self._connector.TraceVariables(Idxs)
+        
+    def SubscribeDebugIECVariable(self, IECPath, callable, *args, **kwargs):
+        """
+        Dispatching use a dictionnary linking IEC variable paths
+        to a WeakKeyDictionary linking 
+        weakly referenced callables to optionnal args
+        """
+        # If no entry exist, create a new one with a fresh WeakKeyDictionary
+        self.IECdebug_callables.setdefault(
+                   IECPath, 
+                   WeakKeyDictionary())[callable]=(args, kwargs)
+        # Rearm anti-rapid-fire timer
+        self.DebugTimer.cancel()
+        self.DebugTimer.start()
+        
+    def Generate_plc_common_main(self):
+        """
+        Use plugins layout given in LocationCFilesAndCFLAGS to
+        generate glue code that dispatch calls to all plugins
+        """
+        # filter location that are related to code that will be called
+        # in retreive, publish, init, cleanup
+        locstrs = map(lambda x:"_".join(map(str,x)),
+           [loc for loc,Cfiles,DoCalls in self.LocationCFilesAndCFLAGS if loc and DoCalls])
+
+        # Generate main, based on template
+        plc_main_code = runtime.code("plc_common_main") % {
+            "calls_prototypes":"\n".join([(
+                  "int __init_%(s)s(int argc,char **argv);\n"+
+                  "void __cleanup_%(s)s();\n"+
+                  "void __retrieve_%(s)s();\n"+
+                  "void __publish_%(s)s();")%{'s':locstr} for locstr in locstrs]),
+            "retrieve_calls":"\n    ".join([
+                  "__retrieve_%s();"%locstr for locstr in locstrs]),
+            "publish_calls":"\n    ".join([ #Call publish in reverse order
+                  "__publish_%s();"%locstrs[i-1] for i in xrange(len(locstrs), 0, -1)]),
+            "init_calls":"\n    ".join([
+                  "init_level=%d; "%i+
+                  "if(res = __init_%s(argc,argv)){"%locstr +
+                  #"printf(\"%s\"); "%locstr + #for debug
+                  "return res;}" for i,locstr in enumerate(locstrs)]),
+            "cleanup_calls":"\n    ".join([
+                  "if(init_level >= %d) "%i+
+                  "__cleanup_%s();"%locstrs[i-1] for i in xrange(len(locstrs), 0, -1)])
+            }
+
+        target_name = self.BeremizRoot.getTargetType().getcontent()["name"]
+        plc_main_code += runtime.code("plc_%s_main"%target_name)
+        
+        return plc_main_code
+
+        
+    def _build(self):
         """
         Method called by user to (re)build SoftPLC and plugin tree
         """
@@ -945,113 +1137,90 @@
         # Eventually create build dir
         if not os.path.exists(buildpath):
             os.mkdir(buildpath)
-        
-        logger.flush()
-        logger.write("Start build in %s\n" % buildpath)
-
+        # There is something to clean
         self.EnableMethod("_Clean", True)
-        self.EnableMethod("_showIECcode", True)
-        
-        # Generate SoftPLC code
-        if not self._Generate_SoftPLC(logger):
-            logger.write_error("SoftPLC code generation failed !\n")
+
+        self.logger.flush()
+        self.logger.write("Start build in %s\n" % buildpath)
+
+        # Generate SoftPLC IEC code
+        IECGenRes = self._Generate_SoftPLC()
+        self.ShowMethod("_showIECcode", True)
+
+        # If IEC code gen fail, bail out.
+        if not IECGenRes:
+            self.logger.write_error("IEC-61131-3 code generation failed !\n")
             return False
 
-
-        #logger.write("SoftPLC code generation successfull\n")
-
-        logger.write("Generating plugins code ...\n")
+        # Reset variable and program list that are parsed from
+        # CSV file generated by IEC2C compiler.
+        self.ResetIECProgramsAndVariables()
         
         # Generate C code and compilation params from plugin hierarchy
+        self.logger.write("Generating plugins C code\n")
         try:
-            LocationCFilesAndCFLAGS,LDFLAGS = self._Generate_C(
+            self.LocationCFilesAndCFLAGS, self.LDFLAGS, ExtraFiles = self._Generate_C(
                 buildpath, 
-                self.PLCGeneratedLocatedVars,
-                logger)
+                self.PLCGeneratedLocatedVars)
         except Exception, exc:
-            logger.write_error("Plugins code generation Failed !\n")
-            logger.write_error(traceback.format_exc())
+            self.logger.write_error("Plugins code generation failed !\n")
+            self.logger.write_error(traceback.format_exc())
             return False
 
-
-        #debug
-        #import pprint
-        #pp = pprint.PrettyPrinter(indent=4)
-        #logger.write("LocationCFilesAndCFLAGS :\n"+pp.pformat(LocationCFilesAndCFLAGS)+"\n")
-        #logger.write("LDFLAGS :\n"+pp.pformat(LDFLAGS)+"\n")
-        
-        # Generate main
-        locstrs = map(lambda x:"_".join(map(str,x)), [loc for loc,Cfiles,DoCalls in LocationCFilesAndCFLAGS if loc and DoCalls])
-        plc_main = runtime.code("plc_common_main") % {
-            "calls_prototypes":"\n".join(
-               ["int __init_%(s)s(int argc,char **argv);\nvoid __cleanup_%(s)s();\nvoid __retrieve_%(s)s();\nvoid __publish_%(s)s();"%
-                {'s':locstr} for locstr in locstrs]),
-            "retrieve_calls":"\n    ".join(["__retrieve_%(s)s();"%{'s':locstr} for locstr in locstrs]),
-            "publish_calls":"\n    ".join(["__publish_%(s)s();"%{'s':locstr} for locstr in locstrs]),
-            "init_calls":"\n    ".join(["init_level++; if(res = __init_%(s)s(argc,argv)) return res;"%{'s':locstr} for locstr in locstrs]),
-            "cleanup_calls":"\n    ".join(["if(init_level-- > 0) __cleanup_%(s)s();"%{'s':locstr} for locstr in locstrs]),
-            "sync_align_ratio":self.BeremizRoot.getSync_Align_Ratio()}
-        target_name = self.BeremizRoot.TargetType.content["name"]
-        plc_main += runtime.code("plc_%s_main"%target_name)
-
-        main_path = os.path.join(buildpath, "main.c" )
-        f = open(main_path,'w')
-        f.write(plc_main)
-        f.close()
-        # First element is necessarely root
-        LocationCFilesAndCFLAGS[0][1].insert(0,(main_path, self.CFLAGS))
-        
-        # Compile the resulting code into object files.
-        compiler = self.BeremizRoot.getCompiler()
-        _CFLAGS = self.BeremizRoot.getCFLAGS()
-        linker = self.BeremizRoot.getLinker()
-        _LDFLAGS = self.BeremizRoot.getLDFLAGS()
-        obns = []
-        objs = []
-        for Location, CFilesAndCFLAGS, DoCalls in LocationCFilesAndCFLAGS:
-            if Location:
-                logger.write("Plugin : " + self.GetChildByIECLocation(Location).GetCurrentName() + " " + str(Location)+"\n")
-            else:
-                logger.write("PLC :\n")
-                
-            for CFile, CFLAGS in CFilesAndCFLAGS:
-                bn = os.path.basename(CFile)
-                obn = os.path.splitext(bn)[0]+".o"
-                obns.append(obn)
-                logger.write("   [CC]  "+bn+" -> "+obn+"\n")
-                objectfilename = os.path.splitext(CFile)[0]+".o"
-
-                status, result, err_result = ProcessLogger(
-                       logger,
-                       "\"%s\" -c \"%s\" -o \"%s\" %s %s"%
-                           (compiler, CFile, objectfilename, _CFLAGS, CFLAGS)
-                       ).spin()
-
-                if status != 0:
-                    logger.write_error("Build failed\n")
-                    return False
-                objs.append(objectfilename)
-        # Link all the object files into one executable
-        logger.write("Linking :\n")
-        exe = self.GetProjectName()
-        if target_name == "Win32":
-            exe += ".exe"
-        exe_path = os.path.join(buildpath, exe)
-        logger.write("   [CC]  " + ' '.join(obns)+" -> " + exe + "\n")
-        status, result, err_result = ProcessLogger(
-               logger,
-               "\"%s\" \"%s\" -o \"%s\" %s"%
-                   (linker,
-                    '" "'.join(objs),
-                    exe_path,
-                    ' '.join(LDFLAGS+[_LDFLAGS]))
-               ).spin()
-        if status != 0:
-            logger.write_error("Build failed\n")
-            self.EnableMethod("_Run", False)
+        # Get temprary directory path
+        extrafilespath = self._getExtraFilesPath()
+        # Remove old directory
+        if os.path.exists(extrafilespath):
+            shutil.rmtree(extrafilespath)
+        # Recreate directory
+        os.mkdir(extrafilespath)
+        # Then write the files
+        for fname,fobject in ExtraFiles:
+            print fname,fobject
+            fpath = os.path.join(extrafilespath,fname)
+            open(fpath, "wb").write(fobject.read())
+        # Now we can forget ExtraFiles (will close files object)
+        del ExtraFiles
+
+        # Template based part of C code generation
+        # files are stacked at the beginning, as files of plugin tree root
+        for generator, filename, name in [
+           # debugger code
+           (self.Generate_plc_debugger, "plc_debugger.c", "Debugger"),
+           # init/cleanup/retrieve/publish, run and align code
+           (self.Generate_plc_common_main,"plc_common_main.c","Common runtime")]:
+            try:
+                # Do generate
+                code = generator()
+                code_path = os.path.join(buildpath,filename)
+                open(code_path, "w").write(code)
+                # Insert this file as first file to be compiled at root plugin
+                self.LocationCFilesAndCFLAGS[0][1].insert(0,(code_path, self.plcCFLAGS))
+            except Exception, exc:
+                self.logger.write_error(name+" generation failed !\n")
+                self.logger.write_error(traceback.format_exc())
+                return False
+
+        self.logger.write("C code generated successfully.\n")
+
+        # Get current or fresh builder
+        builder = self.GetBuilder()
+        if builder is None:
+            self.logger.write_error("Fatal : cannot get builder.\n")
             return False
-        
-        self.EnableMethod("_Run", True)
+
+        # Build
+        try:
+            if not builder.build() :
+                self.logger.write_error("C Build failed.\n")
+                return False
+        except Exception, exc:
+            self.logger.write_error("C Build crashed !\n")
+            self.logger.write_error(traceback.format_exc())
+            return False
+
+        # Update GUI status about need for transfer
+        self.CompareLocalAndRemotePLC()
         return True
     
     def ShowError(self, logger, from_location, to_location):
@@ -1061,8 +1230,8 @@
             start = (from_location[0] - start_row, from_location[1] - start_col)
             end = (to_location[0] - start_row, to_location[1] - start_col)
             self.PLCEditor.ShowError(infos, start, end)
-            
-    def _showIECcode(self, logger):
+
+    def _showIECcode(self):
         plc_file = self._getIECcodepath()
         new_dialog = wx.Frame(self.AppFrame)
         ST_viewer = TextViewer(new_dialog, "", None, None)
@@ -1076,14 +1245,9 @@
             
         new_dialog.Show()
 
-    def _editIECrawcode(self, logger):
+    def _editIECrawcode(self):
         new_dialog = wx.Frame(self.AppFrame)
         
-        buildpath = self._getBuildPath()
-        # Eventually create build dir
-        if not os.path.exists(buildpath):
-            os.mkdir(buildpath)
-        
         controler = MiniTextControler(self._getIECrawcodepath())
         ST_viewer = TextViewer(new_dialog, "", None, controler)
         #ST_viewer.Enable(False)
@@ -1092,7 +1256,7 @@
             
         new_dialog.Show()
 
-    def _EditPLC(self, logger):
+    def _EditPLC(self):
         if self.PLCEditor is None:
             self.RefreshPluginsBlockLists()
             def _onclose():
@@ -1108,83 +1272,245 @@
             self.PLCEditor._onsave = _onsave
             self.PLCEditor.Show()
 
-    def _Clean(self, logger):
+    def _Clean(self):
         if os.path.isdir(os.path.join(self._getBuildPath())):
-            logger.write("Cleaning the build directory\n")
+            self.logger.write("Cleaning the build directory\n")
             shutil.rmtree(os.path.join(self._getBuildPath()))
         else:
-            logger.write_error("Build directory already clean\n")
-        self.EnableMethod("_showIECcode", False)
+            self.logger.write_error("Build directory already clean\n")
+        self.ShowMethod("_showIECcode", False)
         self.EnableMethod("_Clean", False)
-        self.EnableMethod("_Run", False)
-    
-    def _Run(self, logger):
-        command_start_plc = os.path.join(self._getBuildPath(),self.GetProjectName() + exe_ext)
-        if os.path.isfile(command_start_plc):
-            has_gui_plugin = False
-            for PlugChild in self.IterChilds():
-                has_gui_plugin |= PlugChild.IsGUIPlugin()
-            logger.write("Starting PLC\n")
-            def this_plc_finish_callback(*args):
-                if self.runningPLC is not None:
-                    self.runningPLC = None
-                    self.reset_finished()
-            self.runningPLC = ProcessLogger(
-               logger,
-               command_start_plc,
-               finish_callback = this_plc_finish_callback,
-               no_gui=wx.Platform != '__WXMSW__' or not has_gui_plugin)
-            self.EnableMethod("_Clean", False)
-            self.EnableMethod("_Run", False)
-            self.EnableMethod("_Stop", True)
-            self.EnableMethod("_build", False)
+        self.CompareLocalAndRemotePLC()
+
+    ############# Real PLC object access #############
+    def UpdateMethodsFromPLCStatus(self):
+        # Get PLC state : Running or Stopped
+        # TODO : use explicit status instead of boolean
+        if self._connector is not None:
+            status = self._connector.GetPLCstatus()
+            self.logger.write("PLC is %s\n"%status)
         else:
-            logger.write_error("%s doesn't exist\n" %command_start_plc)
-
-    def reset_finished(self):
-        self.EnableMethod("_Clean", True)
-        self.EnableMethod("_Run", True)
-        self.EnableMethod("_Stop", False)
-        self.EnableMethod("_build", True)
-
-    def _Stop(self, logger):
-        if self.runningPLC is not None:
-            logger.write("Stopping PLC\n")
-            was_runningPLC = self.runningPLC 
-            self.runningPLC = None
-            was_runningPLC.kill()
-            self.reset_finished()
+            status = "Disconnected"
+        for args in {
+               "Started":[("_Run", False),
+                          ("_Debug", False),
+                          ("_Stop", True)],
+               "Stopped":[("_Run", True),
+                          ("_Debug", True),
+                          ("_Stop", False)],
+               "Empty":  [("_Run", False),
+                          ("_Debug", False),
+                          ("_Stop", False)],
+               "Dirty":  [("_Run", True),
+                          ("_Debug", True),
+                          ("_Stop", False)],
+               "Disconnected":  [("_Run", False),
+                                 ("_Debug", False),
+                                 ("_Stop", False)],
+               }.get(status,[]):
+            self.ShowMethod(*args)
+        
+    def _Run(self):
+        """
+        Start PLC
+        """
+        if self._connector.StartPLC():
+            self.logger.write("Starting PLC\n")
+        else:
+            self.logger.write_error("Couldn't start PLC !\n")
+        self.UpdateMethodsFromPLCStatus()
+
+    def _Debug(self): 
+        """
+        Start PLC (Debug Mode)
+        """
+        if self.GetIECProgramsAndVariables() and self._connector.StartPLC():
+            self.logger.write("Starting PLC (debug mode)\n")
+            # TODO : laucnch PLCOpenEditor in Debug Mode
+            self.logger.write_warning("Debug mode for PLCopenEditor not implemented\n")
+            self.logger.write_warning("Starting alternative test GUI\n")
+            # TODO : laucnch PLCOpenEditor in Debug Mode
+        else:
+            self.logger.write_error("Couldn't start PLC debug !\n")
+        self.UpdateMethodsFromPLCStatus()
+       
+    def _Stop(self):
+        """
+        Stop PLC
+        """
+        if self._connector.StopPLC():
+            self.logger.write("Stopping PLC\n")
+        else:
+            self.logger.write_error("Couldn't stop PLC !\n")
+        self.UpdateMethodsFromPLCStatus()
+
+    def _Connect(self):
+        # don't accept re-connetion is already connected
+        if self._connector is not None:
+            self.logger.write_error("Already connected. Please disconnect\n")
+            return
+        
+        # Get connector uri
+        uri = self.\
+              BeremizRoot.\
+              getTargetType().\
+              getcontent()["value"].\
+              getConnection().\
+              getURI_location().\
+              strip()
+
+        # if uri is empty launch discovery dialog
+        if uri == "":
+            # Launch Service Discovery dialog
+            dia = DiscoveryDialog(self.AppFrame)
+            dia.ShowModal()
+            uri = dia.GetResult()
+            # Nothing choosed or cancel button
+            if uri is None:
+                return
+            else:
+                self.\
+                BeremizRoot.\
+                getTargetType().\
+                getcontent()["value"].\
+                getConnection().\
+                setURI_location(uri)
+       
+        # Get connector from uri
+        try:
+            self._connector = connectors.ConnectorFactory(uri, self)
+        except Exception, msg:
+            self.logger.write_error("Exception while connecting %s!\n"%uri)
+            self.logger.write_error(traceback.format_exc())
+
+        # Did connection success ?
+        if self._connector is None:
+            # Oups.
+            self.logger.write_error("Connection failed to %s!\n"%uri)
+        else:
+            self.ShowMethod("_Connect", False)
+            self.ShowMethod("_Disconnect", True)
+            self.ShowMethod("_Transfer", True)
+
+            self.CompareLocalAndRemotePLC()
+            self.UpdateMethodsFromPLCStatus()
+
+    def CompareLocalAndRemotePLC(self):
+        if self._connector is None:
+            return
+        # We are now connected. Update button status
+        MD5 = self.GetLastBuildMD5()
+        # Check remote target PLC correspondance to that md5
+        if MD5 is not None:
+            if not self._connector.MatchMD5(MD5):
+                self.logger.write_warning(
+                   "Latest build do not match with target, please transfer.\n")
+                self.EnableMethod("_Transfer", True)
+            else:
+                self.logger.write(
+                   "Latest build match target, no transfer needed.\n")
+                self.EnableMethod("_Transfer", True)
+                #self.EnableMethod("_Transfer", False)
+        else:
+            self.logger.write_warning(
+                "Cannot compare latest build to target. Please build.\n")
+            self.EnableMethod("_Transfer", False)
+
+
+    def _Disconnect(self):
+        self._connector = None
+        self.ShowMethod("_Transfer", False)
+        self.ShowMethod("_Connect", True)
+        self.ShowMethod("_Disconnect", False)
+        self.UpdateMethodsFromPLCStatus()
+        
+    def _Transfer(self):
+        # Get the last build PLC's 
+        MD5 = self.GetLastBuildMD5()
+        
+        # Check if md5 file is empty : ask user to build PLC 
+        if MD5 is None :
+            self.logger.write_error("Failed : Must build before transfer.\n")
+            return False
+
+        # Compare PLC project with PLC on target
+        if self._connector.MatchMD5(MD5):
+            self.logger.write(
+                "Latest build already match current target. Transfering anyway...\n")
+
+        # Get temprary directory path
+        extrafilespath = self._getExtraFilesPath()
+        extrafiles = [(name, open(os.path.join(extrafilespath, name), 
+                                  'rb').read()) \
+                      for name in os.listdir(extrafilespath) \
+                      if not name=="CVS"]
+
+        for filename, unused in extrafiles:
+            print filename
+
+        # Send PLC on target
+        builder = self.GetBuilder()
+        if builder is not None:
+            data = builder.GetBinaryCode()
+            if data is not None :
+                if self._connector.NewPLC(MD5, data, extrafiles):
+                    self.logger.write("Transfer completed successfully.\n")
+                else:
+                    self.logger.write_error("Transfer failed\n")
+            else:
+                self.logger.write_error("No PLC to transfer (did build success ?)\n")
+        self.UpdateMethodsFromPLCStatus()
 
     PluginMethods = [
-        {"bitmap" : os.path.join("images", "editPLC"),
+        {"bitmap" : opjimg("editPLC"),
          "name" : "Edit PLC",
          "tooltip" : "Edit PLC program with PLCOpenEditor",
          "method" : "_EditPLC"},
-        {"bitmap" : os.path.join("images", "Build"),
+        {"bitmap" : opjimg("Build"),
          "name" : "Build",
          "tooltip" : "Build project into build folder",
          "method" : "_build"},
-        {"bitmap" : os.path.join("images", "Clean"),
+        {"bitmap" : opjimg("Clean"),
          "name" : "Clean",
+         "enabled" : False,
          "tooltip" : "Clean project build folder",
          "method" : "_Clean"},
-        {"bitmap" : os.path.join("images", "Run"),
+        {"bitmap" : opjimg("Run"),
          "name" : "Run",
-         "enabled" : False,
-         "tooltip" : "Run PLC from build folder",
+         "shown" : False,
+         "tooltip" : "Start PLC",
          "method" : "_Run"},
-        {"bitmap" : os.path.join("images", "Stop"),
+        {"bitmap" : opjimg("Debug"),
+         "name" : "Debug",
+         "shown" : False,
+         "tooltip" : "Start PLC (debug mode)",
+         "method" : "_Debug"},
+        {"bitmap" : opjimg("Stop"),
          "name" : "Stop",
-         "enabled" : False,
+         "shown" : False,
          "tooltip" : "Stop Running PLC",
          "method" : "_Stop"},
-        {"bitmap" : os.path.join("images", "ShowIECcode"),
+        {"bitmap" : opjimg("Connect"),
+         "name" : "Connect",
+         "tooltip" : "Connect to the target PLC",
+         "method" : "_Connect"},
+        {"bitmap" : opjimg("Transfer"),
+         "name" : "Transfer",
+         "shown" : False,
+         "tooltip" : "Transfer PLC",
+         "method" : "_Transfer"},
+        {"bitmap" : opjimg("Disconnect"),
+         "name" : "Disconnect",
+         "shown" : False,
+         "tooltip" : "Disconnect from PLC",
+         "method" : "_Disconnect"},
+        {"bitmap" : opjimg("ShowIECcode"),
          "name" : "Show code",
-         "enabled" : False,
+         "shown" : False,
          "tooltip" : "Show IEC code generated by PLCGenerator",
          "method" : "_showIECcode"},
-        {"bitmap" : os.path.join("images", "editIECrawcode"),
+        {"bitmap" : opjimg("editIECrawcode"),
          "name" : "Append code",
          "tooltip" : "Edit raw IEC code added to code generated by PLCGenerator",
-         "method" : "_editIECrawcode"}
+         "method" : "_editIECrawcode"},
     ]
--- a/plugins/c_ext/c_ext.py	Tue Aug 12 16:27:07 2008 +0200
+++ b/plugins/c_ext/c_ext.py	Wed Aug 20 00:11:40 2008 +0200
@@ -188,7 +188,7 @@
         return ""
     
     _View = None
-    def _OpenView(self, logger):
+    def _OpenView(self):
         if not self._View:
             def _onclose():
                 self._View = None
@@ -222,7 +222,7 @@
         self.CFileBuffer.CurrentSaved()
         return True
 
-    def PlugGenerate_C(self, buildpath, locations, logger):
+    def PlugGenerate_C(self, buildpath, locations):
         """
         Generate C code
         @param current_location: Tupple containing plugin IEC location : %I0.0.4.5 => (0,0,4,5)
@@ -359,7 +359,7 @@
 
     PlugChildsTypes = [("C_File",_Cfile, "C file")]
     
-    def PlugGenerate_C(self, buildpath, locations, logger):
+    def PlugGenerate_C(self, buildpath, locations):
         return [],"",False
 
 
--- a/plugins/canfestival/canfestival.py	Tue Aug 12 16:27:07 2008 +0200
+++ b/plugins/canfestival/canfestival.py	Wed Aug 20 00:11:40 2008 +0200
@@ -30,6 +30,14 @@
           <xsd:attribute name="CAN_Baudrate" type="xsd:string" use="required"/>
           <xsd:attribute name="NodeId" type="xsd:string" use="required"/>
           <xsd:attribute name="Sync_Align" type="xsd:integer" use="optional" default="0"/>
+          <xsd:attribute name="Sync_Align_Ratio" use="optional" default="50">
+            <xsd:simpleType>
+                <xsd:restriction base="xsd:integer">
+                    <xsd:minInclusive value="1"/>
+                    <xsd:maxInclusive value="99"/>
+                </xsd:restriction>
+            </xsd:simpleType>
+          </xsd:attribute>
         </xsd:complexType>
       </xsd:element>
     </xsd:schema>
@@ -73,7 +81,7 @@
                                    [])           # options
             dialog.Destroy()
     _View = None
-    def _OpenView(self, logger):
+    def _OpenView(self):
         if not self._View:
             def _onclose():
                 self._View = None
@@ -103,7 +111,7 @@
     def OnPlugSave(self):
         return self.SaveCurrentInFile(self.GetSlaveODPath())
 
-    def PlugGenerate_C(self, buildpath, locations, logger):
+    def PlugGenerate_C(self, buildpath, locations):
         """
         Generate C code
         @param current_location: Tupple containing plugin IEC location : %I0.0.4.5 => (0,0,4,5)
@@ -158,7 +166,7 @@
         self.LoadProject(self.PlugPath())
 
     _View = None
-    def _OpenView(self, logger):
+    def _OpenView(self):
         if not self._View:
             def _onclose():
                 self._View = None
@@ -171,16 +179,16 @@
             self._View._onsave = _onsave
             self._View.Show()
 
-    def _ShowMasterGenerated(self, logger):
+    def _ShowMasterGenerated(self):
         buildpath = self._getBuildPath()
         # Eventually create build dir
         if not os.path.exists(buildpath):
-            logger.write_error("Error: No PLC built\n")
+            self.logger.write_error("Error: No PLC built\n")
             return
         
         masterpath = os.path.join(buildpath, "MasterGenerated.od")
         if not os.path.exists(masterpath):
-            logger.write_error("Error: No Master generated\n")
+            self.logger.write_error("Error: No Master generated\n")
             return
         
         new_dialog = objdictedit(None, filesOpen=[masterpath])
@@ -207,7 +215,7 @@
         self.SetRoot(self.PlugPath())
         return self.SaveProject() is not None
 
-    def PlugGenerate_C(self, buildpath, locations, logger):
+    def PlugGenerate_C(self, buildpath, locations):
         """
         Generate C code
         @param current_location: Tupple containing plugin IEC location : %I0.0.4.5 => (0,0,4,5)
@@ -264,7 +272,7 @@
                         return infos    
         return infos
 
-    def PlugGenerate_C(self, buildpath, locations, logger):
+    def PlugGenerate_C(self, buildpath, locations):
         
         format_dict = {"locstr" : "_".join(map(str,self.GetCurrentLocation())),
                        "candriver" : self.CanFestivalInstance.getCAN_Driver(),
@@ -328,15 +336,16 @@
             else:
                 # Slave node
                 align = child_data.getSync_Align()
+                align_ratio=child_data.getSync_Align_Ratio()
                 if align > 0:
                     format_dict["post_sync"] += (
                         "static int %s_CalCount = 0;\n"%(nodename)+
                         "static void %s_post_sync(CO_Data* d){\n"%(nodename)+
                         "    if(%s_CalCount < %d){\n"%(nodename, align)+
                         "        %s_CalCount++;\n"%(nodename)+
-                        "        align_tick(1);\n"+
+                        "        align_tick(-1);\n"+
                         "    }else{\n"+
-                        "        align_tick(0);\n"+
+                        "        align_tick(%d);\n"%(align_ratio)+
                         "    }\n"+
                         "}\n")
                     format_dict["post_sync_register"] += (
--- a/plugins/canfestival/cf_runtime.c	Tue Aug 12 16:27:07 2008 +0200
+++ b/plugins/canfestival/cf_runtime.c	Wed Aug 20 00:11:40 2008 +0200
@@ -77,12 +77,11 @@
 
 void __cleanup_%(locstr)s()
 {
-    %(nodes_close)s
-    
     // Stop timer thread
-    if(init_level-- > 0)
+    if(init_level-- > 0){
         StopTimerLoop(&Exit);
-
+        %(nodes_close)s
+   }
     #if !defined(WIN32) || defined(__CYGWIN__)
    		TimerCleanup();
     #endif
--- a/plugins/svgui/svgui.py	Tue Aug 12 16:27:07 2008 +0200
+++ b/plugins/svgui/svgui.py	Wed Aug 20 00:11:40 2008 +0200
@@ -116,9 +116,6 @@
             self.CreateNewInterface()
             self.SetFilePath(filepath)
 
-    def IsGUIPlugin(self):
-        return True
-
     def GetElementIdFromName(self, name):
         element = self.GetElementByName(name)
         if element is not None:
@@ -126,7 +123,7 @@
         return None
 
     _View = None
-    def _OpenView(self, logger):
+    def _OpenView(self):
         if not self._View:
             def _onclose():
                 self._View = None
@@ -137,7 +134,7 @@
             self._View._onsave = _onsave
             self._View.Show()
 
-    def _ImportSVG(self, logger):
+    def _ImportSVG(self):
         if not self._View:
             dialog = wx.FileDialog(self.GetPlugRoot().AppFrame, "Choose a SVG file", os.getcwd(), "",  "SVG files (*.svg)|*.svg|All files|*.*", wx.OPEN)
             if dialog.ShowModal() == wx.ID_OK:
@@ -145,10 +142,10 @@
                 if os.path.isfile(svgpath):
                     shutil.copy(svgpath, os.path.join(self.PlugPath(), "gui.svg"))
                 else:
-                    logger.write_error("No such SVG file: %s\n"%svgpath)
+                    self.logger.write_error("No such SVG file: %s\n"%svgpath)
             dialog.Destroy()
 
-    def _ImportXML(self, logger):
+    def _ImportXML(self):
         if not self._View:
             dialog = wx.FileDialog(self.GetPlugRoot().AppFrame, "Choose a XML file", os.getcwd(), "",  "XML files (*.xml)|*.xml|All files|*.*", wx.OPEN)
             if dialog.ShowModal() == wx.ID_OK:
@@ -156,7 +153,7 @@
                 if os.path.isfile(xmlpath):
                     shutil.copy(xmlpath, os.path.join(self.PlugPath(), "gui.xml"))
                 else:
-                    logger.write_error("No such XML file: %s\n"%xmlpath)
+                    self.logger.write_error("No such XML file: %s\n"%xmlpath)
             dialog.Destroy()
 
     PluginMethods = [
@@ -178,7 +175,7 @@
         self.SaveXMLFile(os.path.join(self.PlugPath(), "gui.xml"))
         return True
     
-    def PlugGenerate_C(self, buildpath, locations, logger):
+    def PlugGenerate_C(self, buildpath, locations):
         progname = "SVGUI_%s"%"_".join(map(str, self.GetCurrentLocation()))
         
         doc = SVGDocument(self.GetSVGFilePath())
@@ -186,10 +183,15 @@
         window_size = (int(float(root_element.GetAttribute("width"))),
                        int(float(root_element.GetAttribute("height"))))
 
-        svgfilepath = self.GetSVGFilePath()
-        xmlfilepath = self.GetFilePath()
-        shutil.copy(svgfilepath, buildpath)
-        shutil.copy(xmlfilepath, buildpath)
+#        svgfilepath = self.GetSVGFilePath()
+#        xmlfilepath = self.GetFilePath()
+#        shutil.copy(svgfilepath, buildpath)
+#        shutil.copy(xmlfilepath, buildpath)
+        
+        SVGFilePath = self.GetSVGFilePath()
+        SVGFileBaseName = os.path.split(SVGFilePath)[1]
+        FilePath = self.GetFilePath()
+        FileBaseName = os.path.split(FilePath)[1]
         
         generator = _SVGUICGenerator(self, self.GetElementsByType(), 
                                      os.path.split(self.GetSVGFilePath())[1], 
@@ -202,17 +204,17 @@
             cxx_flags = "-I..\\..\\wxPython-src-2.8.7.1\\bld\\lib\\wx\\include\\msw-unicode-release-2.8 -I..\\..\\wxPython-src-2.8.7.1\\include -I..\\..\\wxPython-src-2.8.7.1\\contrib\\include -I..\\..\\matiec\\lib -DWXUSINGDLL -D__WXMSW__ -mthreads"
             libs = "\"..\\lib\\libwxsvg.a\" \"..\\lib\\libwxsvg_agg.a\" \"..\\lib\\libagg.a\" \"..\\lib\\libaggplatformwin32.a\" \"..\\lib\\libaggfontwin32tt.a\" -L..\\..\\wxPython-src-2.8.7.1\\bld\\lib -mno-cygwin -mwindows -mthreads  -mno-cygwin -mwindows -Wl,--subsystem,windows -mwindows -lwx_mswu_richtext-2.8 -lwx_mswu_aui-2.8 -lwx_mswu_xrc-2.8 -lwx_mswu_qa-2.8 -lwx_mswu_html-2.8 -lwx_mswu_adv-2.8 -lwx_mswu_core-2.8 -lwx_baseu_xml-2.8 -lwx_baseu_net-2.8 -lwx_baseu-2.8"
         else:
-            status, result, err_result = ProcessLogger(logger, "wx-config --cxxflags", no_stdout=True).spin()
+            status, result, err_result = ProcessLogger(self.logger, "wx-config --cxxflags", no_stdout=True).spin()
             if status:
-                logger.write_error("Unable to get wx cxxflags\n")
+                self.logger.write_error("Unable to get wx cxxflags\n")
             cxx_flags = result.strip() + " -I../matiec/lib"
             
-            status, result, err_result = ProcessLogger(logger, "wx-config --libs", no_stdout=True).spin()
+            status, result, err_result = ProcessLogger(self.logger, "wx-config --libs", no_stdout=True).spin()
             if status:
-                logger.write_error("Unable to get wx libs\n")
+                self.logger.write_error("Unable to get wx libs\n")
             libs = result.strip() + " -lwxsvg"
         
-        return [(Gen_C_file, cxx_flags)],libs,True
+        return [(Gen_C_file, cxx_flags)],libs,True,(SVGFileBaseName, file(SVGFilePath, "rb")), (FileBaseName, file(FilePath, "rb"))
     
     def BlockTypesFactory(self):
         
@@ -330,7 +332,8 @@
         self.Controler = controler
 
     def GenerateProgramHeadersPublicVars(self):
-        text = """    void OnPlcOutEvent(wxEvent& event);
+        text = """
+    void OnPlcOutEvent(wxEvent& event);
 
     void Retrieve();
     void Publish();
@@ -382,7 +385,6 @@
         text += self.GenerateIECVars()
         
         text += """IMPLEMENT_APP_NO_MAIN(SVGViewApp);
-IMPLEMENT_WX_THEME_SUPPORT;
 SVGViewApp *myapp = NULL;
 wxSemaphore MyInitSem;
 
@@ -412,12 +414,14 @@
 THREAD_RETURN_TYPE InitWxEntry(void* args)
 {
   wxEntry(myargc,myargv);
+  MyInitSem.Post();
   return 0;
 }
 
 """
 
-        text += """bool SVGViewApp::OnInit()
+        text += """
+bool SVGViewApp::OnInit()
 {
   #ifndef __WXMSW__
     setlocale(LC_NUMERIC, "C");
@@ -448,6 +452,12 @@
 
 void __cleanup_%(location)s()
 {
+  if(myapp){
+      wxCloseEvent event(wxEVT_CLOSE_WINDOW);
+      myapp->frame->AddPendingEvent(event);
+      myapp = NULL;
+  }
+  MyInitSem.Wait();
 }
 
 void __retrieve_%(location)s()
@@ -506,9 +516,8 @@
     def GenerateProgramInitFrame(self):
         text = """MainFrame::MainFrame(wxWindow *parent, const wxString& title, const wxPoint& pos,const wxSize& size, long style): wxFrame(parent, wxID_ANY, title, pos, size, style)
 {
-  wxFileName apppath(wxTheApp->argv[0]);
-  wxFileName svgfilepath(apppath.GetPath(), wxT("%s"));
-  wxFileName xmlfilepath(apppath.GetPath(), wxT("%s"));
+  wxFileName svgfilepath(wxTheApp->argv[1], wxT("%s"));
+  wxFileName xmlfilepath(wxTheApp->argv[1], wxT("%s"));
 
   m_svgCtrl = new Program(this);
   if (m_svgCtrl->LoadFiles(svgfilepath.GetFullPath(), xmlfilepath.GetFullPath()))
@@ -522,8 +531,7 @@
   }
   else
   {
-    printf("Error while opening files\\n");
-    exit(0);
+    printf("Error while opening SVGUI files\\n");
   }
 }
 
@@ -545,13 +553,19 @@
         current_location = "_".join(map(str, self.CurrentLocation))
         for element in self.Elements:
             element_type = GetElementType(element)
-            element_lock = """  if (COMPARE_AND_SWAP_VAL(&in_state_%d, CHANGED, GUI_BUSY) == CHANGED ||
+            element_lock = """
+  if (COMPARE_AND_SWAP_VAL(&in_state_%d, CHANGED, GUI_BUSY) == CHANGED ||
       COMPARE_AND_SWAP_VAL(&in_state_%d, UNCHANGED, GUI_BUSY) == UNCHANGED) {
 """%(element.getid(), element.getid())
-            element_unlock = """    COMPARE_AND_SWAP_VAL(&in_state_%d, GUI_BUSY, CHANGED);
+            element_unlock = """
+    COMPARE_AND_SWAP_VAL(&in_state_%d, GUI_BUSY, CHANGED);
+    event.Skip();
+  }else{
+      /* re post event for idle */
+      AddPendingEvent(event);
   }
-  else
-      ProcessEvent(event);
+}
+
 """%element.getid()
             element_name = element.getname()
                 
@@ -562,7 +576,6 @@
                 text += element_lock
                 text += "    _copy__IX%s_%d_1 = button->GetToggle();\n"%(current_location, element.getid())
                 text += element_unlock
-                text += "  event.Skip();\n}\n\n"
             elif element_type == ITEM_ROTATING:
                 text += """void Program::On%sChanging(wxScrollEvent& event)
 {
@@ -571,7 +584,6 @@
                 text += element_lock
                 text += "    _copy__ID%s_%d_1 = rotating->GetAngle();\n"%(current_location, element.getid())
                 text += element_unlock
-                text += "  event.Skip();\n}\n\n"
             elif element_type == ITEM_NOTEBOOK:
                 text += """void Program::On%sTabChanged(wxNotebookEvent& event)
 {
@@ -580,7 +592,6 @@
                 text += element_lock
                 text += "    _copy__IB%s_%d_1 = notebook->GetCurrentPage();\n"%(current_location, element.getid())
                 text += element_unlock
-                text += "  event.Skip();\n}\n\n"
             elif element_type == ITEM_TRANSFORM:
                 text += """void Program::On%sChanging(wxScrollEvent& event)
 {
@@ -590,7 +601,6 @@
                 text += "    _copy__ID%s_%d_1 = transform->GetX();\n"%(current_location, element.getid())
                 text += "    _copy__ID%s_%d_2 = transform->GetY();\n"%(current_location, element.getid())
                 text += element_unlock
-                text += "  event.Skip();\n}\n\n"
         
         text += "/* OnPlcOutEvent update GUI with provided IEC __Q* PLC output variables */\n"
         text += """void Program::OnPlcOutEvent(wxEvent& event)
@@ -599,7 +609,7 @@
   
   refreshing = true;
 
-  wxMutexGuiEnter();
+
 """
         for element in self.Elements:
             element_type = GetElementType(element)
@@ -659,7 +669,7 @@
 """%texts
             text += "    COMPARE_AND_SWAP_VAL(&out_state_%(id)d, GUI_BUSY, UNCHANGED);\n  }\n"%texts
             
-        text += """  wxMutexGuiLeave();
+        text += """
 
   refreshing = false;
 
@@ -716,7 +726,7 @@
         text += """  /* Replace this with determinist signal if called from RT */
   if (refresh && !refreshing) {
     wxCommandEvent event( EVT_PLC );
-    ProcessEvent(event);
+    AddPendingEvent(event);
     refresh = false;
   }
 };
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/runtime/PLCObject.py	Wed Aug 20 00:11:40 2008 +0200
@@ -0,0 +1,258 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+#This file is part of Beremiz, a Integrated Development Environment for
+#programming IEC 61131-3 automates supporting plcopen standard and CanFestival. 
+#
+#Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
+#
+#See COPYING file for copyrights details.
+#
+#This library is free software; you can redistribute it and/or
+#modify it under the terms of the GNU General Public
+#License as published by the Free Software Foundation; either
+#version 2.1 of the License, or (at your option) any later version.
+#
+#This library is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#General Public License for more details.
+#
+#You should have received a copy of the GNU General Public
+#License along with this library; if not, write to the Free Software
+#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+import Pyro.core as pyro
+from threading import Timer
+import ctypes, os, dl, commands
+#, sys
+#sys.setdlopenflags(dl.RTLD_NOW | dl.RTLD_GLOBAL)
+
+if os.name == ("nt", "ce"):
+    from _ctypes import LoadLibrary as dlopen
+    from _ctypes import FreeLibrary as dlclose
+elif os.name == "posix":
+    from _ctypes import dlopen, dlclose
+
+import os,sys,traceback
+
+lib_ext ={
+     "linux2":".so",
+     "win32":".dll",
+     }.get(sys.platform, "")
+
+class PLCObject(pyro.ObjBase):
+    def __init__(self, workingdir, daemon):
+        pyro.ObjBase.__init__(self)
+        self.workingdir = workingdir
+        self.PLCStatus = "Stopped"
+        self.PLClibraryHandle = None
+        # Creates fake C funcs proxies
+        self._FreePLC()
+        self.daemon = daemon
+        
+        # Get the last transfered PLC if connector must be restart
+        try:
+            self.CurrentPLCFilename=open(
+                             self._GetMD5FileName(),
+                             "r").read().strip() + lib_ext
+        except Exception, e:
+            self.PLCStatus = "Empty"
+            self.CurrentPLCFilename=None
+
+    def _GetMD5FileName(self):
+        return os.path.join(self.workingdir, "lasttransferedPLC.md5")
+
+    def _GetLibFileName(self):
+        return os.path.join(self.workingdir,self.CurrentPLCFilename)
+
+
+    def _LoadNewPLC(self):
+        """
+        Load PLC library
+        Declare all functions, arguments and return values
+        """
+        print "Load PLC"
+        try:
+            self._PLClibraryHandle = dlopen(self._GetLibFileName())
+            self.PLClibraryHandle = ctypes.CDLL(self.CurrentPLCFilename, handle=self._PLClibraryHandle)
+    
+            self._startPLC = self.PLClibraryHandle.startPLC
+            self._startPLC.restype = ctypes.c_int
+            self._startPLC.argtypes = [ctypes.c_int, ctypes.POINTER(ctypes.c_char_p)]
+            
+            self._stopPLC = self.PLClibraryHandle.stopPLC
+            self._stopPLC.restype = None
+    
+            self._ResetDebugVariables = self.PLClibraryHandle.ResetDebugVariables
+            self._ResetDebugVariables.restype = None
+    
+            self._RegisterDebugVariable = self.PLClibraryHandle.ResetDebugVariables
+            self._RegisterDebugVariable.restype = None
+    
+            self._IterDebugData = self.PLClibraryHandle.IterDebugData
+            self._IterDebugData.restype = ctypes.c_void_p
+            self._IterDebugData.argtypes = [ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_char_p)]
+    
+            self._FreeDebugData = self.PLClibraryHandle.FreeDebugData
+            self._FreeDebugData.restype = None
+            return True
+        except:
+            print traceback.format_exc()
+            return False
+
+    def _FreePLC(self):
+        """
+        Unload PLC library.
+        This is also called by __init__ to create dummy C func proxies
+        """
+        # Forget all refs to library
+        self._startPLC = lambda:None
+        self._stopPLC = lambda:None
+        self._ResetDebugVariables = lambda:None
+        self._RegisterDebugVariable = lambda x:None
+        self._IterDebugData = lambda x,y:None
+        self._FreeDebugData = lambda:None
+        self.PLClibraryHandle = None
+        # Unload library explicitely
+        if getattr(self,"_PLClibraryHandle",None) is not None:
+            print "Unload PLC"
+            dlclose(self._PLClibraryHandle)
+            res = self._DetectDirtyLibs()
+        else:
+            res = False
+
+        self._PLClibraryHandle = None
+
+        return res
+
+    def _DetectDirtyLibs(self):
+        # Detect dirty libs
+        # Get lib dependencies (for dirty lib detection)
+        if os.name == "posix":
+            # parasiting libs listed with ldd
+            badlibs = [ toks.split()[0] for toks in commands.getoutput(
+                            "ldd "+self._GetLibFileName()).splitlines() ]
+            for badlib in badlibs:
+                if badlib[:6] in ["libwx_",
+                                  "libwxs",
+                                  "libgtk",
+                                  "libgdk",
+                                  "libatk",
+                                  "libpan",
+                                  "libX11",
+                                  ]:
+                    badhandle = dlopen(badlib, dl.RTLD_NOLOAD)
+                    print "Dirty lib detected :" + badlib
+                    #dlclose(badhandle)
+                    return True
+        return False
+
+    
+    def StartPLC(self):
+        print "StartPLC"
+        if self.CurrentPLCFilename is not None and self.PLCStatus == "Stopped":
+            c_argv = ctypes.c_char_p * len(sys.argv)
+            if self._LoadNewPLC() and self._startPLC(len(sys.argv),c_argv(*sys.argv)) == 0:
+                self.PLCStatus = "Started"
+                return True
+            else:
+                print "_StartPLC did not return 0 !"
+        return False
+
+    def StopPLC(self):
+        if self.PLCStatus == "Started":
+            self._stopPLC()
+            self.PLCStatus = "Stopped"
+            if self._FreePLC():
+                self.PLCStatus = "Dirty"
+            return True
+        return False
+
+    def _Reload(self):
+        self.daemon.shutdown(True)
+        self.daemon.sock.close()
+        os.execv(sys.executable,[sys.executable]+sys.argv[:])
+        # never reached
+        return 0
+
+    def ForceReload(self):
+        # respawn python interpreter
+        Timer(0.1,self._Reload).start()
+        return True
+
+    def GetPLCstatus(self):
+        return self.PLCStatus
+    
+    def NewPLC(self, md5sum, data, extrafiles):
+        print "NewPLC (%s)"%md5sum
+        if self.PLCStatus in ["Stopped", "Empty", "Dirty"]:
+            NewFileName = md5sum + lib_ext
+            extra_files_log = os.path.join(self.workingdir,"extra_files.txt")
+            try:
+                os.remove(os.path.join(self.workingdir,
+                                       self.CurrentPLCFilename))
+                for filename in file(extra_files_log, "r").readlines() + extra_files_log:
+                    try:
+                        os.remove(os.path.join(self.workingdir, filename))
+                    except:
+                        pass
+            except:
+                pass
+                        
+            try:
+                # Create new PLC file
+                open(os.path.join(self.workingdir,NewFileName),
+                     'wb').write(data)
+        
+                # Store new PLC filename based on md5 key
+                open(self._GetMD5FileName(), "w").write(md5sum)
+        
+                # Then write the files
+                log = file(extra_files_log, "w")
+                for fname,fdata in extrafiles:
+                    fpath = os.path.join(self.workingdir,fname)
+                    open(fpath, "wb").write(fdata)
+                    log.write(fname+'\n')
+
+                # Store new PLC filename
+                self.CurrentPLCFilename = NewFileName
+            except:
+                print traceback.format_exc()
+                return False
+            if self.PLCStatus == "Empty":
+                self.PLCStatus = "Stopped"
+            return True
+        return False
+
+    def MatchMD5(self, MD5):
+        try:
+            last_md5 = open(self._GetMD5FileName(), "r").read()
+            return last_md5 == MD5
+        except:
+            return False
+    
+    def SetTraceVariablesList(self, idxs):
+        """
+        Call ctype imported function to append 
+        these indexes to registred variables in PLC debugger
+        """
+        # keep a copy of requested idx
+        self._Idxs = idxs[:]
+        self._ResetDebugVariables()
+        for idx in idxs:
+            self._RegisterDebugVariable(idx)
+
+    def GetTraceVariables(self):
+        """
+        Return a list of variables, corresponding to the list of requiered idx
+        """
+        self._WaitDebugData()
+
+        for idx in self._Idxs:
+            buffer=self._IterDebugData()
+        self._FreeDebugData()
+        
+        
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/runtime/ServicePublisher.py	Wed Aug 20 00:11:40 2008 +0200
@@ -0,0 +1,41 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+#This file is part of Beremiz, a Integrated Development Environment for
+#programming IEC 61131-3 automates supporting plcopen standard and CanFestival. 
+#
+#Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
+#
+#See COPYING file for copyrights details.
+#
+#This library is free software; you can redistribute it and/or
+#modify it under the terms of the GNU General Public
+#License as published by the Free Software Foundation; either
+#version 2.1 of the License, or (at your option) any later version.
+#
+#This library is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#General Public License for more details.
+#
+#You should have received a copy of the GNU General Public
+#License along with this library; if not, write to the Free Software
+#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+import Zeroconf
+
+class PublishService():
+    def __init__(self):
+        self.server = Zeroconf.Zeroconf()
+
+    def ConfigureService(self, type, name, address, port, description):
+        self.newservice = Zeroconf.ServiceInfo(type,
+                                  name,
+                                  address,
+                                  port,
+                                  weight = 0, # weight: weight of the service
+                                  priority= 0, # priority: priority of the service
+                                  properties = description)
+    
+    def PublishService(self):
+        self.server.registerService(self.newservice)
\ No newline at end of file
--- a/runtime/__init__.py	Tue Aug 12 16:27:07 2008 +0200
+++ b/runtime/__init__.py	Wed Aug 20 00:11:40 2008 +0200
@@ -8,3 +8,6 @@
         return open(filename).read()
     else:
         return "#error %s target not implemented !!!\n"%name
+
+from PLCObject import PLCObject
+import ServicePublisher
--- a/runtime/plc_Linux_main.c	Tue Aug 12 16:27:07 2008 +0200
+++ b/runtime/plc_Linux_main.c	Wed Aug 20 00:11:40 2008 +0200
@@ -3,6 +3,17 @@
 #include <time.h>
 #include <signal.h>
 #include <stdlib.h>
+#include <pthread.h> 
+
+long AtomicCompareExchange(long* atomicvar,long exchange, long compared)
+{
+    return __sync_val_compare_and_swap(atomicvar, compared, exchange);
+}
+
+//long AtomicExchange(long* atomicvar,long exchange)
+//{
+//    return __sync_lock_test_and_set(atomicvar, exchange);
+//}
 
 void PLC_GetTime(IEC_TIME *CURRENT_TIME)
 {
@@ -41,15 +52,16 @@
 	}	
     timer_settime (PLC_timer, 0, &timerValues, NULL);
 }
-
+//
 void catch_signal(int sig)
 {
-  signal(SIGTERM, catch_signal);
+//  signal(SIGTERM, catch_signal);
   signal(SIGINT, catch_signal);
   printf("Got Signal %d\n",sig);
+  exit(0);
 }
 
-int main(int argc,char **argv)
+int startPLC(int argc,char **argv)
 {
     struct sigevent sigev;
     /* Translate PLC's microseconds to Ttick nanoseconds */
@@ -66,15 +78,35 @@
         PLC_SetTimer(Ttick,Ttick);
         
         /* install signal handler for manual break */
-        signal(SIGTERM, catch_signal);
+//        signal(SIGTERM, catch_signal);
         signal(SIGINT, catch_signal);
-        /* Wait some signal */
-        pause();
-        /* Stop the PLC */
-        PLC_SetTimer(0,0);
+    }else{
+        return 1;
     }
-    __cleanup();
-    timer_delete (PLC_timer);
-    
     return 0;
 }
+
+int stopPLC()
+{
+    /* Stop the PLC */
+    PLC_SetTimer(0,0);
+    timer_delete (PLC_timer);
+    __cleanup();
+}
+
+pthread_mutex_t DebugLock = PTHREAD_MUTEX_INITIALIZER;
+
+/* from plc_debugger.c */
+void WaitDebugData()
+{
+    /* Wait signal from PLC thread */
+    pthread_mutex_lock(&DebugLock);
+}
+ 
+/* Called by PLC thread when debug_publish finished
+ * This is supposed to unlock debugger thread in WaitDebugData*/
+void InitiateDebugTransfer()
+{
+    /* signal debugger thread to continue*/
+    pthread_mutex_unlock(&DebugLock);
+}
--- a/runtime/plc_Win32_main.c	Tue Aug 12 16:27:07 2008 +0200
+++ b/runtime/plc_Win32_main.c	Wed Aug 20 00:11:40 2008 +0200
@@ -3,7 +3,15 @@
 #include <time.h>
 #include <windows.h>
 
-int localcount = 0;
+long AtomicCompareExchange(long* atomicvar,long exchange, long compared)
+{
+    return InterlockedCompareExchange(atomicvar, exchange, compared);
+}
+
+//long AtomicExchange(long* atomicvar,long exchange)
+//{
+//    return InterlockedExchange(atomicvar, exchange);    
+//}
 
 struct _timeb timetmp;
 void PLC_GetTime(IEC_TIME *CURRENT_TIME)
--- a/runtime/plc_common_main.c	Tue Aug 12 16:27:07 2008 +0200
+++ b/runtime/plc_common_main.c	Wed Aug 20 00:11:40 2008 +0200
@@ -17,6 +17,7 @@
 #define maxval(a,b) ((a>b)?a:b)
 
 #include "iec_types.h"
+/*#include "stdio.h" /* For debug */
 
 /*
  * Functions and variables provied by generated C softPLC
@@ -25,13 +26,14 @@
 void config_init__(void);
 
 /*
- *  Functions and variables to export to generated C softPLC
+ *  Functions and variables to export to generated C softPLC and plugins
  **/
  
 IEC_TIME __CURRENT_TIME;
+int __tick = 0;
 
-static int tick = 0;
-static int init_level=0;
+static int init_level = 0;
+static int Debugging = 1;
 
 /*
  * Prototypes of functions exported by plugins 
@@ -44,13 +46,16 @@
 void __run()
 {
     %(retrieve_calls)s
+
+    if(Debugging) __retrieve_debug();
     
-	/*
-	printf("run tick = %%d\n", tick + 1);
-	*/
-    config_run__(tick++);
+    config_run__(__tick);
+
+    if(Debugging) __publish_debug();
     
     %(publish_calls)s
+
+    __tick++;
 }
 
 /*
@@ -88,13 +93,14 @@
 #define mod %%
 /*
  * Call this on each external sync, 
+ * @param sync_align_ratio 0->100 : align ratio, < 0 : no align, calibrate period 
  **/
-void align_tick(int calibrate)
+void align_tick(int sync_align_ratio)
 {
 	/*
 	printf("align_tick(%%d)\n", calibrate);
 	*/
-	if(calibrate){
+	if(sync_align_ratio < 0){ /* Calibration */
 		if(calibration_count == CALIBRATED)
 			/* Re-calibration*/
 			calibration_count = NOT_CALIBRATED;
@@ -102,7 +108,7 @@
 			/* Calibration start, get time*/
 			PLC_GetTime(&cal_begin);
 		calibration_count++;
-	}else{
+	}else{ /* do alignment (if possible) */
 		if(calibration_count >= 0){
 			/* End of calibration */
 			/* Get final time */
@@ -135,7 +141,7 @@
 			PLC_GetTime(&now);
 			elapsed = (now.tv_sec - __CURRENT_TIME.tv_sec) * 1000000000 + now.tv_nsec - __CURRENT_TIME.tv_nsec;
 			if(Nticks > 0){
-				PhaseCorr = elapsed - (Ttick + FreqCorr/Nticks)*%(sync_align_ratio)d/100; /* to be divided by Nticks */
+				PhaseCorr = elapsed - (Ttick + FreqCorr/Nticks)*sync_align_ratio/100; /* to be divided by Nticks */
 				Tcorr = Ttick + (PhaseCorr + FreqCorr) / Nticks;
 				if(Nticks < 2){
 					/* When Sync source period is near Tick time */
@@ -144,9 +150,9 @@
 				}else{
 					PeriodicTcorr = Tcorr; 
 				}
-			}else if(tick > last_tick){
-				last_tick = tick;
-				PhaseCorr = elapsed - (Tsync*%(sync_align_ratio)d/100);
+			}else if(__tick > last_tick){
+				last_tick = __tick;
+				PhaseCorr = elapsed - (Tsync*sync_align_ratio/100);
 				PeriodicTcorr = Tcorr = Ttick + PhaseCorr + FreqCorr;
 			}else{
 				/*PLC did not run meanwhile. Nothing to do*/
@@ -157,3 +163,15 @@
 		}
 	}
 }
+
+int suspendDebug()
+{
+    /* Prevent PLC to enter debug code */
+    Debugging = 0;
+}
+
+int resumeDebug()
+{
+    /* Let PLC enter debug code */
+    Debugging = 1;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/runtime/plc_debug.c	Wed Aug 20 00:11:40 2008 +0200
@@ -0,0 +1,151 @@
+/*
+ * DEBUGGER code
+ * 
+ * On "publish", when buffer is free, debugger stores arbitrary variables 
+ * content into, and mark this buffer as filled
+ * 
+ * 
+ * Buffer content is read asynchronously, (from non real time part), 
+ * and then buffer marked free again.
+ *  
+ * 
+ * */
+#include "iec_types_all.h"
+#include "POUS.h"
+/*for memcpy*/
+#include <string.h>
+
+#define BUFFER_SIZE 1024
+#define MAX_SUBSCRIBTION %(subscription_table_count)d
+
+/* Atomically accessed variable for buffer state */
+#define BUFFER_FREE 0
+#define BUFFER_BUSY 1
+static long buffer_state = BUFFER_FREE;
+
+/* The buffer itself */
+char debug_buffer[BUFFER_SIZE];
+
+/* Buffer's cursor*/
+static char* buffer_cursor = debug_buffer;
+
+typedef struct{
+    void* ptrvalue;
+    __IEC_types_enum type;
+}struct_plcvar;
+
+/***
+ * Declare programs 
+ **/
+%(programs_declarations)s
+
+/***
+ * Declare global variables from resources and conf 
+ **/
+%(extern_variables_declarations)s
+
+static int subscription_table[MAX_SUBSCRIBTION];
+static int* latest_subscription = subscription_table;
+static int* subscription_cursor = subscription_table;
+
+struct_plcvar variable_table[%(variables_pointer_type_table_count)d];
+
+void __init_debug()
+{
+%(variables_pointer_type_table_initializer)s
+};
+
+void __cleanup_debug()
+{
+}
+
+void __retrieve_debug()
+{
+}
+
+void __publish_debug()
+{
+    /* Lock buffer */
+    long latest_state = AtomicCompareExchange(
+        &buffer_state,
+        BUFFER_FREE,
+        BUFFER_BUSY);
+        
+    /* If buffer was free */
+    if(latest_state == BUFFER_FREE)
+    {
+        int* subscription;
+        
+        /* Reset buffer cursor */
+        buffer_cursor = debug_buffer;
+        
+        /* iterate over subscriptions */
+        for(subscription=subscription_table;
+            subscription < latest_subscription;
+            subscription++)
+        {
+            /* get variable descriptor */
+            struct_plcvar* my_var = &variable_table[*subscription];
+            char* next_cursor;
+            /* get variable size*/
+            USINT size = __get_type_enum_size(my_var->type);
+            /* compute next cursor positon*/
+            next_cursor = buffer_cursor + size;
+            /* if buffer not full */
+            if(next_cursor < debug_buffer + BUFFER_SIZE)
+            {
+                /* copy data to the buffer */
+                memcpy(buffer_cursor, my_var->ptrvalue, size);
+                /* increment cursor according size*/
+                buffer_cursor = next_cursor;
+            }else{
+                /*TODO : signal overflow*/
+            }
+        }
+
+        /* Reset buffer cursor again (for IterDebugData)*/
+        buffer_cursor = debug_buffer;
+        subscription_cursor = subscription_table;
+        
+        /* Trigger asynchronous transmission (returns immediately) */
+        InitiateDebugTransfer(); /* size */
+    }
+}
+
+void RegisterDebugVariable(int idx)
+{
+    /*If subscription table not full */
+    if(latest_subscription - subscription_table < MAX_SUBSCRIBTION)
+    {
+        *(latest_subscription++) = idx;
+        /* TODO pre-calc buffer size and signal overflow*/
+    }else{
+        /*TODO : signal subscription overflow*/
+    }
+}
+
+void ResetDebugVariables(void)
+{
+    latest_subscription = subscription_table;
+}
+
+void FreeDebugData()
+{
+    /* atomically mark buffer as free */
+    long latest_state = AtomicCompareExchange(
+        &buffer_state,
+        BUFFER_BUSY,
+        BUFFER_FREE);
+}
+
+void* IterDebugData(int* idx, const char **type_name)
+{
+    if(subscription_cursor < latest_subscription){
+        *idx = *subscription_cursor;
+        struct_plcvar* my_var = &variable_table[*subscription_cursor++];
+        *type_name = __get_type_enum_name(my_var->type);
+        return my_var->ptrvalue;
+    }
+    return NULL;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/targets/Linux/XSD	Wed Aug 20 00:11:40 2008 +0200
@@ -0,0 +1,13 @@
+
+                  <xsd:element name="Linux">
+                    <xsd:complexType>
+                      <xsd:sequence>
+                        <xsd:element name="Connection">
+                          <xsd:complexType>
+                            <xsd:attribute name="URI_location" type="xsd:string" use="optional" default=""/>
+                          </xsd:complexType>
+                        </xsd:element>
+                      </xsd:sequence>
+                      %(toolchain_gcc)s
+                    </xsd:complexType>
+                  </xsd:element>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/targets/Linux/__init__.py	Wed Aug 20 00:11:40 2008 +0200
@@ -0,0 +1,5 @@
+from .. import toolchain_gcc
+
+class Linux_target(toolchain_gcc):
+    extension = ".so"
+    CustomLDFLAGS = ["-shared"]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/targets/Rtai/XSD	Wed Aug 20 00:11:40 2008 +0200
@@ -0,0 +1,14 @@
+
+                  <xsd:element name="Rtai">
+                    <xsd:complexType>
+                      <xsd:sequence>
+                        <xsd:element name="Connection">
+                          <xsd:complexType>
+                            <xsd:attribute name="URI_location" type="xsd:string" use="optional" default=""/>
+                          </xsd:complexType>
+                        </xsd:element>
+                      </xsd:sequence>
+                      %(toolchain_gcc)s
+                      <xsd:attribute name="rtai_config" type="xsd:string" use="optional" default="/usr/realtime/"/>
+                    </xsd:complexType>
+                  </xsd:element>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/targets/Rtai/__init__.py	Wed Aug 20 00:11:40 2008 +0200
@@ -0,0 +1,1 @@
+from target_rtai import *
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/targets/Rtai/target_rtai.py	Wed Aug 20 00:11:40 2008 +0200
@@ -0,0 +1,3 @@
+class rtai_target(targets.target_gcc):
+    extensionexe = "exe"
+    extensiondll = "dll"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/targets/Win32/XSD	Wed Aug 20 00:11:40 2008 +0200
@@ -0,0 +1,14 @@
+
+                  <xsd:element name="Win32">
+                    <xsd:complexType>
+                      <xsd:sequence>
+                        <xsd:element name="Connection">
+                          <xsd:complexType>
+                            <xsd:attribute name="URI_location" type="xsd:string" use="optional" default=""/>
+                          </xsd:complexType>
+                        </xsd:element>
+                      </xsd:sequence>
+                      %(toolchain_gcc)s
+                    </xsd:complexType>
+                  </xsd:element>
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/targets/Win32/__init__.py	Wed Aug 20 00:11:40 2008 +0200
@@ -0,0 +1,10 @@
+from .. import toolchain_gcc
+
+class Win32_target(toolchain_gcc):
+    extension = ".dll"
+    CustomLDFLAGS = ["-shared",
+                     "-Wl,--export-all-symbols",
+                     "-Wl,--enable-auto-import",
+                     "-Wl,--whole-archive",
+                     "-Wl,--no-whole-archive",
+                     "-Wl,--exclude-libs,All"]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/targets/XSD_toolchain_gcc	Wed Aug 20 00:11:40 2008 +0200
@@ -0,0 +1,5 @@
+
+          <xsd:attribute name="Compiler" type="xsd:string" use="optional" default="gcc"/>
+          <xsd:attribute name="CFLAGS" type="xsd:string" use="required"/>
+          <xsd:attribute name="Linker" type="xsd:string" use="optional" default="ld"/>
+          <xsd:attribute name="LDFLAGS" type="xsd:string" use="required"/>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/targets/Xenomai/XSD	Wed Aug 20 00:11:40 2008 +0200
@@ -0,0 +1,14 @@
+
+                 <xsd:element name="Xenomai">
+                    <xsd:complexType>
+                      <xsd:sequence>
+                        <xsd:element name="Connection">
+                          <xsd:complexType>
+                            <xsd:attribute name="URI_location" type="xsd:string" use="optional" default=""/>
+                          </xsd:complexType>
+                        </xsd:element>
+                      </xsd:sequence>
+                      %(toolchain_gcc)s
+                      <xsd:attribute name="xeno_config" type="xsd:string" use="optional" default="/usr/xenomai/"/>
+                    </xsd:complexType>
+                  </xsd:element>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/targets/Xenomai/__init__.py	Wed Aug 20 00:11:40 2008 +0200
@@ -0,0 +1,1 @@
+from target_xenomai import *
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/targets/Xenomai/target_xenomai.py	Wed Aug 20 00:11:40 2008 +0200
@@ -0,0 +1,3 @@
+class xenomai_target(targets.target_gcc):
+    extensionexe = ""
+    extensiondll = ""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/targets/__init__.py	Wed Aug 20 00:11:40 2008 +0200
@@ -0,0 +1,67 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
+#
+#See COPYING file for copyrights details.
+#
+#This library is free software; you can redistribute it and/or
+#modify it under the terms of the GNU General Public
+#License as published by the Free Software Foundation; either
+#version 2.1 of the License, or (at your option) any later version.
+#
+#This library is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#General Public License for more details.
+#
+#You should have received a copy of the GNU General Public
+#License along with this library; if not, write to the Free Software
+#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+# Package initialisation
+#import targets
+
+"""
+Beremiz Targets
+
+- Target are python packages, containing at least one "XSD" file
+- Target class may inherit from a toolchain_(toolchainname)
+- The target folder's name must match to name define in the XSD for TargetType
+"""
+
+from os import listdir, path
+
+_base_path = path.split(__file__)[0]
+
+targets = [name for name in listdir(_base_path) if path.isdir(path.join(_base_path, name)) and name.upper() != "CVS" and not name.startswith("__")]
+toolchains = [name for name in listdir(_base_path) if not path.isdir(path.join(_base_path, name)) and name.upper() != "CVS" and name.endswith(".py") and not name.startswith("__") and not name.endswith(".pyc")]
+
+DictXSD_toolchain = {}
+DictXSD_target = {}
+
+targetchoices = ""
+
+# Get all xsd toolchains
+for toolchain in toolchains :
+     toolchainname = path.splitext(toolchain)[0]
+     xsdfilename = path.join(_base_path, "XSD_%s"%(toolchainname))
+     if path.isfile(xsdfilename):
+         xsd_toolchain_string = ""
+         for line in open(xsdfilename).readlines():
+             xsd_toolchain_string += line
+         DictXSD_toolchain[toolchainname] = xsd_toolchain_string
+
+# Get all xsd targets 
+for targetname in targets:
+    xsdfilename = path.join(_base_path, targetname, "XSD")
+    if path.isfile(xsdfilename):
+        xsd_target_string = ""
+        for line in open(xsdfilename).readlines():
+            xsd_target_string += line
+        DictXSD_target[targetname] = xsd_target_string%DictXSD_toolchain
+
+for target in DictXSD_target.keys():
+    targetchoices += DictXSD_target[target]
+
+from toolchain_gcc import toolchain_gcc
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/targets/toolchain_gcc.py	Wed Aug 20 00:11:40 2008 +0200
@@ -0,0 +1,107 @@
+import os
+from wxPopen import ProcessLogger
+import hashlib
+
+class toolchain_gcc():
+    """
+    This abstract class contains GCC specific code.
+    It cannot be used as this and should be inherited in a target specific
+    class such as target_linux or target_win32
+    """
+    def __init__(self, PuginsRootInstance):
+        self.PuginsRootInstance = PuginsRootInstance
+        self.logger = PuginsRootInstance.logger
+        self.exe = PuginsRootInstance.GetProjectName() + self.extension
+        self.buildpath = PuginsRootInstance._getBuildPath()
+        self.exe_path = os.path.join(self.buildpath, self.exe)
+        self.md5key = None
+
+    def GetBinaryCode(self):
+        try:
+            return open(self.exe_path, "rb").read()
+        except Exception, e:
+            return None
+        
+    def _GetMD5FileName(self):
+        return os.path.join(self.buildpath, "lastbuildPLC.md5")
+
+    def GetBinaryCodeMD5(self):
+        if self.md5key is not None:
+            return self.md5key
+        else:
+            try:
+                return open(self._GetMD5FileName(), "r").read()
+            except Exception, e:
+                return None
+                
+    
+    def build(self):
+        # Retrieve toolchain user parameters
+        toolchain_params = self.PuginsRootInstance.BeremizRoot.getTargetType().getcontent()["value"]
+        self.compiler = toolchain_params.getCompiler()
+        self._CFLAGS = toolchain_params.getCFLAGS()
+        self.linker = toolchain_params.getLinker()
+        self._LDFLAGS = toolchain_params.getLDFLAGS()
+
+        ######### GENERATE OBJECT FILES ########################################
+        obns = []
+        objs = []
+        for Location, CFilesAndCFLAGS, DoCalls in self.PuginsRootInstance.LocationCFilesAndCFLAGS:
+            if Location:
+                self.logger.write("Plugin : " + self.PuginsRootInstance.GetChildByIECLocation(Location).GetCurrentName() + " " + str(Location)+"\n")
+            else:
+                self.logger.write("PLC :\n")
+                
+            for CFile, CFLAGS in CFilesAndCFLAGS:
+                bn = os.path.basename(CFile)
+                obn = os.path.splitext(bn)[0]+".o"
+                obns.append(obn)
+                self.logger.write("   [CC]  "+bn+" -> "+obn+"\n")
+                objectfilename = os.path.splitext(CFile)[0]+".o"
+                
+                status, result, err_result = ProcessLogger(
+                       self.logger,
+                       "\"%s\" -c \"%s\" -o \"%s\" %s %s"%
+                           (self.compiler, CFile, objectfilename, self._CFLAGS, CFLAGS)
+                       ).spin()
+
+                if status :
+                    self.logger.write_error("C compilation of "+ bn +" failed.\n")
+                    return False
+                objs.append(objectfilename)
+
+        ######### GENERATE library FILE ########################################
+        # Link all the object files into one binary file
+        self.logger.write("Linking :\n")
+        objstring = []
+
+        # Generate list .o files
+        listobjstring = '"' + '"  "'.join(objs) + '"'
+
+        ALLldflags = ' '.join(self.CustomLDFLAGS+self.PuginsRootInstance.LDFLAGS+[self._LDFLAGS])
+
+        self.logger.write("   [CC]  " + ' '.join(obns)+" -> " + self.exe + "\n")
+
+        status, result, err_result = ProcessLogger(
+               self.logger,
+               "\"%s\" %s -o \"%s\" %s"%
+                   (self.linker,
+                    listobjstring,
+                    self.exe_path,
+                    ALLldflags)
+               ).spin()
+        
+        if status :
+            return False
+        else :
+            # Calculate md5 key and get data for the new created PLC
+            data=self.GetBinaryCode()
+            self.md5key = hashlib.md5(data).hexdigest()
+
+            # Store new PLC filename based on md5 key
+            file = open(self._GetMD5FileName(), "w")
+            file.write(self.md5key)
+            file.close()
+        
+        return True
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/linux/autom_ihm_rmll/CFileTest@c_ext/File1@C_File/baseplugin.xml	Wed Aug 20 00:11:40 2008 +0200
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<BaseParams Name="File1" IEC_Channel="0"/>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/linux/autom_ihm_rmll/CFileTest@c_ext/File1@C_File/cfile.xml	Wed Aug 20 00:11:40 2008 +0200
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<CFile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.w3.org/2001/XMLSchema" xsi:schemaLocation="cext_xsd.xsd">
+  <includes>
+<![CDATA[]]>
+  </includes>
+  <variables/>
+  <globals>
+<![CDATA[]]>
+  </globals>
+  <initFunction>
+<![CDATA[]]>
+  </initFunction>
+  <cleanUpFunction>
+<![CDATA[]]>
+  </cleanUpFunction>
+  <retrieveFunction>
+<![CDATA[]]>
+  </retrieveFunction>
+  <publishFunction>
+<![CDATA[]]>
+  </publishFunction>
+</CFile>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/linux/autom_ihm_rmll/CFileTest@c_ext/File1@C_File/plugin.xml	Wed Aug 20 00:11:40 2008 +0200
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<CExtension CFLAGS="" LDFLAGS=""/>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/linux/autom_ihm_rmll/CFileTest@c_ext/baseplugin.xml	Wed Aug 20 00:11:40 2008 +0200
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<BaseParams Name="CFileTest" IEC_Channel="2"/>
--- a/tests/linux/autom_ihm_rmll/beremiz.xml	Tue Aug 12 16:27:07 2008 +0200
+++ b/tests/linux/autom_ihm_rmll/beremiz.xml	Wed Aug 20 00:11:40 2008 +0200
@@ -1,9 +1,8 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<BeremizRoot CFLAGS="" Linker="g++" LDFLAGS="">
+<BeremizRoot>
   <TargetType>
-    <Linux Nice="0"/>
+    <Linux CFLAGS="-g" Linker="g++" LDFLAGS="">
+      <Connection URI_location="PYRO://192.168.0.6:3000"/>
+    </Linux>
   </TargetType>
-  <Connection>
-    <Local/>
-  </Connection>
 </BeremizRoot>
--- a/tests/linux/autom_ihm_rmll/canopen@canfestival/master@CanOpenNode/eds/Slave_2_0.eds	Tue Aug 12 16:27:07 2008 +0200
+++ b/tests/linux/autom_ihm_rmll/canopen@canfestival/master@CanOpenNode/eds/Slave_2_0.eds	Wed Aug 20 00:11:40 2008 +0200
@@ -4,11 +4,11 @@
 FileRevision=1
 EDSVersion=4.0
 Description=
-CreationTime=03:59PM
-CreationDate=06-28-2008
+CreationTime=04:36PM
+CreationDate=07-25-2008
 CreatedBy=CANFestival
-ModificationTime=03:59PM
-ModificationDate=06-28-2008
+ModificationTime=04:36PM
+ModificationDate=07-25-2008
 ModifiedBy=CANFestival
 
 [DeviceInfo]
@@ -117,7 +117,7 @@
 [OptionalObjects]
 SupportedObjects=2
 1=0x1017
-2=0x1280
+2=0x1200
 
 [1017]
 ParameterName=Producer Heartbeat Time
@@ -127,50 +127,43 @@
 DefaultValue=0
 PDOMapping=0
 
-[1280]
-ParameterName=Client SDO 1 Parameter
+[1200]
+ParameterName=Server SDO Parameter
 ObjectType=0x8
-SubNumber=4
-
-[1280sub0]
+SubNumber=3
+
+[1200sub0]
 ParameterName=Number of Entries
 ObjectType=0x7
 DataType=0x0005
 AccessType=ro
-DefaultValue=3
-PDOMapping=0
-
-[1280sub1]
-ParameterName=COB ID Client to Server (Transmit SDO)
-ObjectType=0x7
-DataType=0x0007
-AccessType=rw
-DefaultValue=0
-PDOMapping=0
-
-[1280sub2]
-ParameterName=COB ID Server to Client (Receive SDO)
-ObjectType=0x7
-DataType=0x0007
-AccessType=rw
-DefaultValue=0
-PDOMapping=0
-
-[1280sub3]
-ParameterName=Node ID of the SDO Server
-ObjectType=0x7
-DataType=0x0005
-AccessType=rw
-DefaultValue=0
+DefaultValue=2
+PDOMapping=0
+
+[1200sub1]
+ParameterName=COB ID Client to Server (Receive SDO)
+ObjectType=0x7
+DataType=0x0007
+AccessType=ro
+DefaultValue=$NODEID+0x600
+PDOMapping=0
+
+[1200sub2]
+ParameterName=COB ID Server to Client (Transmit SDO)
+ObjectType=0x7
+DataType=0x0007
+AccessType=ro
+DefaultValue=$NODEID+0x580
 PDOMapping=0
 
 [ManufacturerObjects]
-SupportedObjects=5
+SupportedObjects=6
 1=0x2000
 2=0x2001
 3=0x2002
 4=0x2003
 5=0x2004
+6=0x2005
 
 [2000]
 ParameterName=pump
@@ -211,3 +204,11 @@
 AccessType=rw
 DefaultValue=0
 PDOMapping=1
+
+[2005]
+ParameterName=test64
+ObjectType=0x7
+DataType=0x0015
+AccessType=rw
+DefaultValue=0
+PDOMapping=1
--- a/tests/linux/autom_ihm_rmll/canopen@canfestival/master@CanOpenNode/master.od	Tue Aug 12 16:27:07 2008 +0200
+++ b/tests/linux/autom_ihm_rmll/canopen@canfestival/master@CanOpenNode/master.od	Wed Aug 20 00:11:40 2008 +0200
@@ -1,10 +1,10 @@
 <?xml version="1.0"?>
 <!DOCTYPE PyObject SYSTEM "PyObjects.dtd">
-<PyObject module="node" class="Node" id="158606924">
-<attr name="Profile" type="dict" id="158611084" >
+<PyObject module="node" class="Node" id="158559244">
+<attr name="Profile" type="dict" id="158569716" >
 </attr>
 <attr name="Description" type="string" value="" />
-<attr name="Dictionary" type="dict" id="158610812" >
+<attr name="Dictionary" type="dict" id="158569444" >
   <entry>
     <key type="numeric" value="4096" />
     <val type="numeric" value="0" />
@@ -15,32 +15,32 @@
   </entry>
   <entry>
     <key type="numeric" value="4120" />
-    <val type="list" id="157899276" >
-      <item type="numeric" value="0" />
-      <item type="numeric" value="0" />
-      <item type="numeric" value="0" />
-      <item type="numeric" value="0" />
-    </val>
-  </entry>
-</attr>
-<attr name="SpecificMenu" type="list" id="157899020" >
-</attr>
-<attr name="ParamsDictionary" type="dict" id="158610404" >
-</attr>
-<attr name="UserMapping" type="dict" id="158610132" >
-</attr>
-<attr name="DS302" type="dict" id="157920356" >
+    <val type="list" id="157924460" >
+      <item type="numeric" value="0" />
+      <item type="numeric" value="0" />
+      <item type="numeric" value="0" />
+      <item type="numeric" value="0" />
+    </val>
+  </entry>
+</attr>
+<attr name="SpecificMenu" type="list" id="157227212" >
+</attr>
+<attr name="ParamsDictionary" type="dict" id="158569036" >
+</attr>
+<attr name="UserMapping" type="dict" id="158568764" >
+</attr>
+<attr name="DS302" type="dict" id="157947396" >
   <entry>
     <key type="numeric" value="7968" />
-    <val type="dict" id="157943164" >
+    <val type="dict" id="157949708" >
       <entry>
         <key type="string" value="need" />
         <val type="False" value="" />
       </entry>
       <entry>
         <key type="string" value="values" />
-        <val type="list" id="157899404" >
-          <item type="dict" id="158611628" >
+        <val type="list" id="157924588" >
+          <item type="dict" id="158570260" >
             <entry>
               <key type="string" value="access" />
               <val type="string" value="ro" />
@@ -58,7 +58,7 @@
               <val type="string" value="Number of Entries" />
             </entry>
           </item>
-          <item type="dict" id="158610268" >
+          <item type="dict" id="158568900" >
             <entry>
               <key type="string" value="access" />
               <val type="string" value="rw" />
@@ -94,15 +94,15 @@
   </entry>
   <entry>
     <key type="numeric" value="7969" />
-    <val type="dict" id="158610676" >
+    <val type="dict" id="158569308" >
       <entry>
         <key type="string" value="need" />
         <val type="False" value="" />
       </entry>
       <entry>
         <key type="string" value="values" />
-        <val type="list" id="157899628" >
-          <item type="dict" id="158611900" >
+        <val type="list" id="157924844" >
+          <item type="dict" id="158570532" >
             <entry>
               <key type="string" value="access" />
               <val type="string" value="ro" />
@@ -120,7 +120,7 @@
               <val type="string" value="Number of Entries" />
             </entry>
           </item>
-          <item type="dict" id="158612172" >
+          <item type="dict" id="158570804" >
             <entry>
               <key type="string" value="access" />
               <val type="string" value="rw" />
@@ -156,15 +156,15 @@
   </entry>
   <entry>
     <key type="numeric" value="7970" />
-    <val type="dict" id="158612308" >
+    <val type="dict" id="158570940" >
       <entry>
         <key type="string" value="need" />
         <val type="False" value="" />
       </entry>
       <entry>
         <key type="string" value="values" />
-        <val type="list" id="157899692" >
-          <item type="dict" id="158611220" >
+        <val type="list" id="157924908" >
+          <item type="dict" id="158569852" >
             <entry>
               <key type="string" value="access" />
               <val type="string" value="ro" />
@@ -182,7 +182,7 @@
               <val type="string" value="Number of Entries" />
             </entry>
           </item>
-          <item type="dict" id="158612580" >
+          <item type="dict" id="158571212" >
             <entry>
               <key type="string" value="access" />
               <val type="string" value="rw" />
--- a/tests/linux/autom_ihm_rmll/canopen@canfestival/master@CanOpenNode/nodelist.cpj	Tue Aug 12 16:27:07 2008 +0200
+++ b/tests/linux/autom_ihm_rmll/canopen@canfestival/master@CanOpenNode/nodelist.cpj	Wed Aug 20 00:11:40 2008 +0200
@@ -2,6 +2,6 @@
 NetName=None
 Nodes=0x01
 Node3Present=0x01
-Node3Name=myslave
+Node3Name=MySlave
 Node3DCFName=Slave_2_0.eds
 EDSBaseName=eds
--- a/tests/linux/autom_ihm_rmll/plc.xml	Tue Aug 12 16:27:07 2008 +0200
+++ b/tests/linux/autom_ihm_rmll/plc.xml	Wed Aug 20 00:11:40 2008 +0200
@@ -8,7 +8,7 @@
               productVersion="1"
               creationDateTime="2008-06-28 15:43:31"/>
   <contentHeader name="autom_ihm_rmll"
-                 modificationDateTime="2008-06-28 18:26:40">
+                 modificationDateTime="2008-08-20 00:50:31">
     <coordinateInfo>
       <pageSize x="700" y="1000"/>
       <fbd>
@@ -65,11 +65,15 @@
                 <BOOL/>
               </type>
             </variable>
-            <variable name="full_in" address="%IX1.0.3.8194.0">
-              <type>
-                <BOOL/>
-              </type>
-            </variable>
+          </localVars>
+          <externalVars>
+            <variable name="full_in">
+              <type>
+                <BOOL/>
+              </type>
+            </variable>
+          </externalVars>
+          <localVars>
             <variable name="empty_in" address="%IX1.0.3.8195.0">
               <type>
                 <BOOL/>
@@ -787,7 +791,34 @@
           <task name="matache" interval="00:00:00.100000" priority="0">
             <pouInstance name="moninst" type="main"/>
           </task>
+          <globalVars>
+            <variable name="full_in" address="%IX1.0.3.8194.0">
+              <type>
+                <BOOL/>
+              </type>
+            </variable>
+            <variable name="tyto">
+              <type>
+                <INT/>
+              </type>
+              <initialValue>
+                <simpleValue value="2"/>
+              </initialValue>
+            </variable>
+          </globalVars>
         </resource>
+        <globalVars>
+          <variable name="popy">
+            <type>
+              <INT/>
+            </type>
+          </variable>
+          <variable name="fulfuck" address="%IX1.0.3.8194.0">
+            <type>
+              <BOOL/>
+            </type>
+          </variable>
+        </globalVars>
       </configuration>
     </configurations>
   </instances>