chiark / gitweb /
Update the printerconnections for multiple Doodle3D box support.
authordaid <daid303@gmail.com>
Mon, 23 Dec 2013 12:36:54 +0000 (13:36 +0100)
committerdaid <daid303@gmail.com>
Mon, 23 Dec 2013 12:36:54 +0000 (13:36 +0100)
Cura/gui/printWindow2.py
Cura/gui/sceneView.py
Cura/util/printerConnection/doodle3dConnect.py
Cura/util/printerConnection/dummyConnection.py
Cura/util/printerConnection/printerConnectionBase.py
Cura/util/printerConnection/printerConnectionManager.py

index 3ad0e4f9a45d893bdf3b2e6b25c7b1cc329b55b7..8ce23b00cc7f5f8e3ee8ec4513a2f483c6819118 100644 (file)
@@ -3,14 +3,11 @@ __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AG
 import wx
 import power
 import time
-import os
-import datetime
 
 from wx.lib import buttons
 
 from Cura.util import profile
 from Cura.util import resources
-from Cura.gui.util import webcam
 
 class printWindow(wx.Frame):
        "Main user interface window"
@@ -177,47 +174,8 @@ class printWindow(wx.Frame):
 
                self.UpdateButtonStates()
 
-               if webcam.hasWebcamSupport():
-                       #Need to call the camera class on the GUI thread, or else it won't work. Shame as it hangs the GUI for about 2 seconds.
-                       wx.CallAfter(self._webcamCheck)
-
                self._printerConnection.addCallback(self._doPrinterConnectionUpdate)
 
-       def _webcamCheck(self):
-               self.cam = webcam.webcam()
-               if self.cam.hasCamera():
-                       self.camPage = wx.Panel(self.tabs)
-                       sizer = wx.GridBagSizer(2, 2)
-                       self.camPage.SetSizer(sizer)
-
-                       self.timelapsEnable = wx.CheckBox(self.camPage, -1, _("Enable timelapse movie recording"))
-                       self.timelapsSavePath = wx.TextCtrl(self.camPage, -1, os.path.expanduser('~/timelaps_' + datetime.datetime.now().strftime('%Y-%m-%d_%H-%M') + '.mpg'))
-                       sizer.Add(self.timelapsEnable, pos=(0, 0), span=(1, 2), flag=wx.EXPAND)
-                       sizer.Add(self.timelapsSavePath, pos=(1, 0), span=(1, 2), flag=wx.EXPAND)
-
-                       pages = self.cam.propertyPages()
-                       self.cam.buttons = [self.timelapsEnable, self.timelapsSavePath]
-                       for page in pages:
-                               button = wx.Button(self.camPage, -1, page)
-                               button.index = pages.index(page)
-                               sizer.Add(button, pos=(2, pages.index(page)))
-                               button.Bind(wx.EVT_BUTTON, self.OnWebcamPropertyPageButton)
-                               self.cam.buttons.append(button)
-
-                       self.campreviewEnable = wx.CheckBox(self.camPage, -1, _("Show preview"))
-                       sizer.Add(self.campreviewEnable, pos=(3, 0), span=(1, 2), flag=wx.EXPAND)
-
-                       self.camPreview = wx.Panel(self.camPage)
-                       sizer.Add(self.camPreview, pos=(4, 0), span=(1, 2), flag=wx.EXPAND)
-
-                       self.tabs.AddPage(self.camPage, _("Camera"))
-                       self.camPreview.timer = wx.Timer(self)
-                       self.Bind(wx.EVT_TIMER, self.OnCameraTimer, self.camPreview.timer)
-                       self.camPreview.timer.Start(500)
-                       self.camPreview.Bind(wx.EVT_ERASE_BACKGROUND, self.OnCameraEraseBackground)
-               else:
-                       self.cam = None
-
        def OnPowerWarningChange(self, e):
                type = self.powerManagement.get_providing_power_source_type()
                if type == power.POWER_TYPE_AC and self.powerWarningText.IsShown():
@@ -331,31 +289,6 @@ class printWindow(wx.Frame):
                self.temperatureHeatUp.Enable(self._printerConnection.isAbleToSendDirectCommand())
                self.termInput.Enable(self._printerConnection.isAbleToSendDirectCommand())
 
-       def OnWebcamPropertyPageButton(self, e):
-               self.cam.openPropertyPage(e.GetEventObject().index)
-
-       def OnCameraTimer(self, e):
-               if not self.campreviewEnable.GetValue():
-                       return
-               if self.machineCom is not None and self.machineCom.isPrinting():
-                       return
-               self.cam.takeNewImage()
-               self.camPreview.Refresh()
-
-       def OnCameraEraseBackground(self, e):
-               dc = e.GetDC()
-               if not dc:
-                       dc = wx.ClientDC(self)
-                       rect = self.GetUpdateRegion().GetBox()
-                       dc.SetClippingRect(rect)
-               dc.SetBackground(wx.Brush(self.camPreview.GetBackgroundColour(), wx.SOLID))
-               if self.cam.getLastImage() is not None:
-                       self.camPreview.SetMinSize((self.cam.getLastImage().GetWidth(), self.cam.getLastImage().GetHeight()))
-                       self.camPage.Fit()
-                       dc.DrawBitmap(self.cam.getLastImage(), 0, 0)
-               else:
-                       dc.Clear()
-
 class PrintCommandButton(buttons.GenBitmapButton):
        def __init__(self, parent, commandList, bitmapFilename, size=(20, 20)):
                self.bitmap = wx.Bitmap(resources.getPathForImage(bitmapFilename))
index 9e60a09281323474c104ccf7b7119bc8729f8fc2..a3921c1dd0bbb7505e46b8f3d244d746234cd830 100644 (file)
@@ -227,10 +227,10 @@ class SceneView(openglGui.glGuiPanel):
 
        def OnPrintButton(self, button):
                if button == 1:
-                       connectionEntry = self._printerConnectionManager.getAvailableConnection()
+                       connectionGroup = self._printerConnectionManager.getAvailableGroup()
                        if machineCom.machineIsConnected():
                                self.showPrintWindow()
-                       elif len(removableStorage.getPossibleSDcardDrives()) > 0 and (connectionEntry is None or connectionEntry.priority < 0):
+                       elif len(removableStorage.getPossibleSDcardDrives()) > 0 and (connectionGroup is None or connectionGroup.getPriority() < 0):
                                drives = removableStorage.getPossibleSDcardDrives()
                                if len(drives) > 1:
                                        dlg = wx.SingleChoiceDialog(self, "Select SD drive", "Multiple removable drives have been found,\nplease select your SD card drive", map(lambda n: n[0], drives))
@@ -243,27 +243,46 @@ class SceneView(openglGui.glGuiPanel):
                                        drive = drives[0]
                                filename = self._scene._objectList[0].getName() + '.gcode'
                                threading.Thread(target=self._copyFile,args=(self._gcodeFilename, drive[1] + filename, drive[1])).start()
-                       elif connectionEntry is not None:
-                               connection = connectionEntry.connection
-                               if connectionEntry.window is None or not connectionEntry.window:
-                                       connectionEntry.window = printWindow2.printWindow(connection)
-                               connectionEntry.window.Show()
-                               connectionEntry.window.Raise()
-                               if not connection.loadFile(self._gcodeFilename):
-                                       if connection.isPrinting():
-                                               self.notification.message("Cannot start print, because other print still running.")
-                                       else:
-                                               self.notification.message("Failed to start print...")
+                       elif connectionGroup is not None:
+                               connections = connectionGroup.getAvailableConnections()
+                               if len(connections) < 2:
+                                       connection = connections[0]
+                               else:
+                                       dlg = wx.SingleChoiceDialog(self, "Select the %s connection to use" % (connectionGroup.getName()), "Multiple %s connections found" % (connectionGroup.getName()), map(lambda n: n.getName(), connections))
+                                       if dlg.ShowModal() != wx.ID_OK:
+                                               dlg.Destroy()
+                                               return
+                                       connection = connections[dlg.GetSelection()]
+                                       dlg.Destroy()
+                               self._openPrintWindowForConnection(connection)
                        else:
                                self.showSaveGCode()
                if button == 3:
                        menu = wx.Menu()
                        self.Bind(wx.EVT_MENU, lambda e: self.showPrintWindow(), menu.Append(-1, _("Print with USB")))
+                       connections = self._printerConnectionManager.getAvailableConnections()
+                       menu.connectionMap = {}
+                       for connection in connections:
+                               i = menu.Append(-1, _("Print with %s") % (connection.getName()))
+                               menu.connectionMap[i.GetId()] = connection
+                               self.Bind(wx.EVT_MENU, lambda e: self._openPrintWindowForConnection(e.GetEventObject().connectionMap[e.GetId()]), i)
                        self.Bind(wx.EVT_MENU, lambda e: self.showSaveGCode(), menu.Append(-1, _("Save GCode...")))
                        self.Bind(wx.EVT_MENU, lambda e: self._showSliceLog(), menu.Append(-1, _("Slice engine log...")))
                        self.PopupMenu(menu)
                        menu.Destroy()
 
+       def _openPrintWindowForConnection(self, connection):
+               print '_openPrintWindowForConnection', connection.getName()
+               if connection.window is None or not connection.window:
+                       connection.window = printWindow2.printWindow(connection)
+               connection.window.Show()
+               connection.window.Raise()
+               if not connection.loadFile(self._gcodeFilename):
+                       if connection.isPrinting():
+                               self.notification.message("Cannot start print, because other print still running.")
+                       else:
+                               self.notification.message("Failed to start print...")
+
        def showPrintWindow(self):
                if self._gcodeFilename is None:
                        return
@@ -907,16 +926,16 @@ class SceneView(openglGui.glGuiPanel):
                glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
 
        def OnPaint(self,e):
-               connectionEntry = self._printerConnectionManager.getAvailableConnection()
+               connectionGroup = self._printerConnectionManager.getAvailableGroup()
                if machineCom.machineIsConnected():
                        self.printButton._imageID = 6
                        self.printButton._tooltip = _("Print")
-               elif len(removableStorage.getPossibleSDcardDrives()) > 0 and (connectionEntry is None or connectionEntry.priority < 0):
+               elif len(removableStorage.getPossibleSDcardDrives()) > 0 and (connectionGroup is None or connectionGroup.getPriority() < 0):
                        self.printButton._imageID = 2
                        self.printButton._tooltip = _("Toolpath to SD")
-               elif connectionEntry is not None:
-                       self.printButton._imageID = connectionEntry.icon
-                       self.printButton._tooltip = _("Print with %s") % (connectionEntry.name)
+               elif connectionGroup is not None:
+                       self.printButton._imageID = connectionGroup.getIconID()
+                       self.printButton._tooltip = _("Print with %s") % (connectionGroup.getName())
                else:
                        self.printButton._imageID = 3
                        self.printButton._tooltip = _("Save toolpath")
index 822a5042a2108ee76efa823aae925eb3ed87f38f..bb03cd75e58b82815cba81adb2de184475e7d183 100644 (file)
@@ -8,17 +8,100 @@ import time
 
 from Cura.util.printerConnection import printerConnectionBase
 
-#Class to connect and print files with the doodle3d.com wifi box
-# Auto-detects if the Doodle3D box is available with a printer
-class doodle3dConnect(printerConnectionBase.printerConnectionBase):
+class doodle3dConnectionGroup(printerConnectionBase.printerConnectionGroup):
        PRINTER_LIST_HOST = 'connect.doodle3d.com'
        PRINTER_LIST_PATH = '/api/list.php'
 
        def __init__(self):
-               super(doodle3dConnect, self).__init__()
+               super(doodle3dConnectionGroup, self).__init__("Doodle3D")
+               self._http = None
+               self._host = self.PRINTER_LIST_HOST
+               self._connectionMap = {}
+
+               self._thread = threading.Thread(target=self._doodle3DThread)
+               self._thread.daemon = True
+               self._thread.start()
+
+       def getAvailableConnections(self):
+               return filter(lambda c: c.isAvailable(), self._connectionMap.values())
+
+       def getIconID(self):
+               return 27
+
+       def getPriority(self):
+               return 100
+
+       def __cmp__(self, other):
+               return self.getPriority() - other.getPriority()
+
+       def __repr__(self):
+               return self.name
+
+       def _doodle3DThread(self):
+               self._waitDelay = 0
+               while True:
+                       printerList = self._request('GET', self.PRINTER_LIST_PATH)
+                       if not printerList or type(printerList) is not dict or 'data' not in printerList or type(printerList['data']) is not list:
+                               #Check if we are connected to the Doodle3D box in access point mode, as this gives an
+                               # invalid reply on the printer list API
+                               printerList = {'data': [{'localip': 'draw.doodle3d.com'}]}
+
+                       #Add the 192.168.5.1 IP to the list of printers to check, as this is the LAN port IP, which could also be available.
+                       # (connect.doodle3d.com also checks for this IP in the javascript code)
+                       printerList['data'].append({'localip': '192.168.5.1'})
+
+                       #Check the status of each possible IP, if we find a valid box with a printer connected. Use that IP.
+                       for possiblePrinter in printerList['data']:
+                               if possiblePrinter['localip'] not in self._connectionMap:
+                                       status = self._request('GET', '/d3dapi/config/?network.cl.wifiboxid=', host=possiblePrinter['localip'])
+                                       if status and 'data' in status and 'network.cl.wifiboxid' in status['data']:
+                                               self._connectionMap[possiblePrinter['localip']] = doodle3dConnect(possiblePrinter['localip'], status['data']['network.cl.wifiboxid'], self)
+
+                       # Delay a bit more after every request. This so we do not stress the connect.doodle3d.com api too much
+                       if self._waitDelay < 10:
+                               self._waitDelay += 1
+                       time.sleep(self._waitDelay * 60)
+
+       def _request(self, method, path, postData = None, host = None):
+               if host is None:
+                       host = self._host
+               if self._http is None or self._http.host != host:
+                       self._http = httpclient.HTTPConnection(host, timeout=30)
+
+               try:
+                       if postData is not None:
+                               self._http.request(method, path, urllib.urlencode(postData), {"Content-type": "application/x-www-form-urlencoded", "User-Agent": "Cura Doodle3D connection"})
+                       else:
+                               self._http.request(method, path, headers={"Content-type": "application/x-www-form-urlencoded", "User-Agent": "Cura Doodle3D connection"})
+               except:
+                       self._http.close()
+                       return None
+               try:
+                       response = self._http.getresponse()
+                       responseText = response.read()
+               except:
+                       self._http.close()
+                       return None
+               try:
+                       response = json.loads(responseText)
+               except ValueError:
+                       self._http.close()
+                       return None
+               if response['status'] != 'success':
+                       return False
+
+               return response
+
+#Class to connect and print files with the doodle3d.com wifi box
+# Auto-detects if the Doodle3D box is available with a printer
+class doodle3dConnect(printerConnectionBase.printerConnectionBase):
+       def __init__(self, host, name, group):
+               super(doodle3dConnect, self).__init__(name)
 
                self._http = None
-               self._host = None
+               self._group = group
+               self._host = host
+
                self._isAvailable = False
                self._printing = False
                self._fileBlocks = []
@@ -29,6 +112,7 @@ class doodle3dConnect(printerConnectionBase.printerConnectionBase):
                self._hotendTemperature = [None] * 4
                self._bedTemperature = None
                self._errorCount = 0
+               self._interruptSleep = False
 
                self.checkThread = threading.Thread(target=self._doodle3DThread)
                self.checkThread.daemon = True
@@ -72,6 +156,7 @@ class doodle3dConnect(printerConnectionBase.printerConnectionBase):
                self._progressLine = 0
                self._blockIndex = 0
                self._printing = True
+               self._interruptSleep = True
 
        #Abort the previously loaded print file
        def cancelPrint(self):
@@ -103,6 +188,7 @@ class doodle3dConnect(printerConnectionBase.printerConnectionBase):
                if not self._isAvailable or self._printing:
                        return
                self._commandList.append(command)
+               self._interruptSleep = True
 
        # Get the connection status string. This is displayed to the user and can be used to communicate
        #  various information to the user.
@@ -130,41 +216,12 @@ class doodle3dConnect(printerConnectionBase.printerConnectionBase):
                return self._bedTemperature
 
        def _doodle3DThread(self):
-               waitDelay = 0
                while True:
-                       while self._host is None:
-                               printerList = self._request('GET', self.PRINTER_LIST_PATH, host=self.PRINTER_LIST_HOST)
-                               if not printerList or type(printerList) is not dict or 'data' not in printerList or type(printerList['data']) is not list:
-                                       #Check if we are connected to the Doodle3D box in access point mode, as this gives an
-                                       # invalid reply on the printer list API
-                                       printerList = {'data': [{'localip': 'draw.doodle3d.com'}]}
-
-                               #Add the 192.168.5.1 IP to the list of printers to check, as this is the LAN port IP, which could also be available.
-                               # (connect.doodle3d.com also checks for this IP in the javascript code)
-                               printerList['data'].append({'localip': '192.168.5.1'})
-
-                               #Check the status of each possible IP, if we find a valid box with a printer connected. Use that IP.
-                               for possiblePrinter in printerList['data']:
-                                       status = self._request('GET', '/d3dapi/info/status', host=possiblePrinter['localip'])
-                                       if status and 'data' in status:
-                                               self._host = possiblePrinter['localip']
-                                               break
-
-                               if self._host is None:
-                                       #If we cannot find a doodle3d box, delay a minute and request the list again.
-                                       # This so we do not stress the connect.doodle3d.com api too much
-                                       if waitDelay < 10:
-                                               waitDelay += 1
-                                       time.sleep(waitDelay * 60)
-                               else:
-                                       #If we found a doodle3D box, reset the wait delay, so we can find it again in case it gets lost
-                                       self._errorCount = 0
-                                       waitDelay = 0
-
                        stateReply = self._request('GET', '/d3dapi/info/status')
                        if stateReply is None or not stateReply:
-                               # No API, wait 15 seconds before looking for Doodle3D again.
+                               # No API, wait 5 seconds before looking for Doodle3D again.
                                # API gave back an error (this can happen if the Doodle3D box is connecting to the printer)
+                               # The Doodle3D box could also be offline, if we reach a high enough errorCount then assume the box is gone.
                                self._errorCount += 1
                                if self._errorCount > 10:
                                        self._host = None
@@ -172,15 +229,18 @@ class doodle3dConnect(printerConnectionBase.printerConnectionBase):
                                                self._printing = False
                                                self._isAvailable = False
                                                self._doCallback()
-                               time.sleep(15)
+                                       self._sleep(15)
+                               else:
+                                       self._sleep(3)
                                continue
                        if stateReply['data']['state'] == 'disconnected':
-                               # No printer connected
+                               # No printer connected, we do not have a printer available, but the Doodle3D box is there.
+                               # So keep trying to find a printer connected to it.
                                if self._isAvailable:
                                        self._printing = False
                                        self._isAvailable = False
                                        self._doCallback()
-                               time.sleep(5)
+                               self._sleep(15)
                                continue
                        self._errorCount = 0
 
@@ -199,7 +259,7 @@ class doodle3dConnect(printerConnectionBase.printerConnectionBase):
                                                if self._request('POST', '/d3dapi/printer/print', {'gcode': self._fileBlocks[self._blockIndex], 'start': 'True', 'first': 'True'}):
                                                        self._blockIndex += 1
                                                else:
-                                                       time.sleep(1)
+                                                       self._sleep(1)
                                        else:
                                                self._printing = False
                                else:
@@ -207,9 +267,9 @@ class doodle3dConnect(printerConnectionBase.printerConnectionBase):
                                                if self._request('POST', '/d3dapi/printer/print', {'gcode': self._commandList[0], 'start': 'True', 'first': 'True'}):
                                                        self._commandList.pop(0)
                                                else:
-                                                       time.sleep(1)
+                                                       self._sleep(1)
                                        else:
-                                               time.sleep(5)
+                                               self._sleep(5)
                        elif stateReply['data']['state'] == 'printing':
                                if self._printing:
                                        if self._blockIndex < len(self._fileBlocks):
@@ -219,11 +279,11 @@ class doodle3dConnect(printerConnectionBase.printerConnectionBase):
                                                                        self._blockIndex += 1
                                                                else:
                                                                        #Cannot send new block, wait a bit, so we do not overload the API
-                                                                       time.sleep(15)
+                                                                       self._sleep(15)
                                                                        break
                                        else:
                                                #If we are no longer sending new GCode delay a bit so we request the status less often.
-                                               time.sleep(5)
+                                               self._sleep(5)
                                        if 'current_line' in stateReply['data']:
                                                self._progressLine = stateReply['data']['current_line']
                                else:
@@ -234,9 +294,16 @@ class doodle3dConnect(printerConnectionBase.printerConnectionBase):
                                                self._blockIndex = 1
                                                self._progressLine = stateReply['data']['current_line']
                                                self._lineCount = stateReply['data']['total_lines']
-                                       time.sleep(5)
+                                       self._sleep(5)
                        self._doCallback()
 
+       def _sleep(self, timeOut):
+               while timeOut > 0.0:
+                       if not self._interruptSleep:
+                               time.sleep(0.1)
+                       timeOut -= 0.1
+               self._interruptSleep = False
+
        def _request(self, method, path, postData = None, host = None):
                if host is None:
                        host = self._host
index ae2d5e88e80a93889f47f1e320317c31d44c3f72..4d8dee0f113601404ae6609cec0a4e78f3dbfdf0 100644 (file)
@@ -8,10 +8,18 @@ import time
 
 from Cura.util.printerConnection import printerConnectionBase
 
+class dummyConnectionGroup(printerConnectionBase.printerConnectionGroup):
+       def __init__(self):
+               super(dummyConnectionGroup, self).__init__("Dummy")
+               self._list = [dummyConnection("Dummy 1"), dummyConnection("Dummy 2")]
+
+       def getAvailableConnections(self):
+               return self._list
+
 #Dummy printer class which is always
 class dummyConnection(printerConnectionBase.printerConnectionBase):
-       def __init__(self):
-               super(dummyConnection, self).__init__()
+       def __init__(self, name):
+               super(dummyConnection, self).__init__(name)
 
                self._printing = False
                self._lineCount = 0
index c886f6e71a289706fc48a624dc936e09e0e8fab2..41594fef2e9ac871a2c5201109f57d59c3e3743c 100644 (file)
@@ -2,6 +2,28 @@ __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AG
 
 import traceback
 
+class printerConnectionGroup(object):
+       def __init__(self, name):
+               self._name = name
+
+       def getAvailableConnections(self):
+               return []
+
+       def getName(self):
+               return self._name
+
+       def getIconID(self):
+               return 5
+
+       def getPriority(self):
+               return -1
+
+       def __cmp__(self, other):
+               return self.getPriority() - other.getPriority()
+
+       def __repr__(self):
+               return self.name
+
 #Base class for different printer connection implementations.
 # A printer connection can connect to printers in different ways, trough network, USB or carrier pigeons.
 # Each printer connection has different capabilities that you can query with the "has" functions.
@@ -9,8 +31,13 @@ import traceback
 # Each printer connection has callback objects that receive status updates from the printer when information changes.
 
 class printerConnectionBase(object):
-       def __init__(self):
+       def __init__(self, name):
                self._callbackList = []
+               self._name = name
+               self.window = None
+
+       def getName(self):
+               return self._name
 
        #Load the file into memory for printing, returns True on success
        def loadFile(self, filename):
index c377627520e410d4bdf614be3146f6c67ded81c6..674b86aec465ba5f12fa1c93c8dd2b8c41c721b0 100644 (file)
@@ -4,33 +4,26 @@ from Cura.util import version
 from Cura.util.printerConnection import dummyConnection
 from Cura.util.printerConnection import doodle3dConnect
 
-class connectionEntry(object):
-       def __init__(self, name, priority, icon, connection):
-               self.name = name
-               self.priority = priority
-               self.icon = icon
-               self.connection = connection
-               self.window = None
-
-       def __cmp__(self, other):
-               return self.priority - other.priority
-
-       def __repr__(self):
-               return self.name
-
 class PrinterConnectionManager(object):
        def __init__(self):
-               self._connectionList = []
+               self._groupList = []
                if version.isDevVersion():
-                       self._connectionList.append(connectionEntry('Dummy', -1, 5, dummyConnection.dummyConnection()))
-               self._connectionList.append(connectionEntry('Doodle3D', 100, 27, doodle3dConnect.doodle3dConnect()))
+                       self._groupList.append(dummyConnection.dummyConnectionGroup())
+               #self._groupList.append(doodle3dConnect.doodle3dConnectionGroup())
 
                #Sort the connections by highest priority first.
-               self._connectionList.sort(reverse=True)
+               self._groupList.sort(reverse=True)
 
        #Return the highest priority available connection.
-       def getAvailableConnection(self):
-               for e in self._connectionList:
-                       if e.connection.isAvailable():
-                               return e
+       def getAvailableGroup(self):
+               for g in self._groupList:
+                       if len(g.getAvailableConnections()) > 0:
+                               return g
                return None
+
+       #Return all available connections.
+       def getAvailableConnections(self):
+               ret = []
+               for e in self._groupList:
+                       ret += e.getAvailableConnections()
+               return ret