/// <summary>
        /// Retrieve all the plate types. The list of PlateTypes is cached for _cacheLifetime min.
        /// </summary>
        /// <param name="robot">The robot to find the plate types for.</param>
        /// <returns>An array of plate types, or null if there are none.</returns>
        public IPlateType[] GetPlateTypes(IRobot robot)
        {
            // Check arguments - do it up front to avoid possible inconsistencies later
            if (robot == null)
            {
                throw new System.NullReferenceException("robot must not be null");
            }

            // Log the call
            if (_log.IsDebugEnabled)
            {
                string msg = "Called " + this + ".GetPlateTypes(robot=" + robot.ToString() + ")";
                _log.Debug(msg);
            }

            // Return cached values if appropriate
            if ((_plateTypes != null) && (System.DateTime.Now.Ticks <= _plateTypesCacheExpires))
            {
                _log.Debug("GetPlateTypes() using cached response");
                return(_plateTypes);
            }

            _log.Debug("GetPlateTypes() refreshing cache");

            /*
             * platetypeid |             strplatetype             | intcolumns | introws | intsubpositions | intsubpositionheight | strbarcodepattern | intdefaultscheduleplan
             * -------------+--------------------------------------+------------+---------+-----------------+----------------------+-------------------+------------------------
             * 1 | Greiner, central shelf only          |         12 |       8 |               1 |                 2107 | 4413xxxxxxxx      |                      1
             * 2 | OPPF Full Greiner, all three shelves |         12 |       8 |               3 |                 2107 | 3313xxxxxxxx      |                      1
             * 3 | Fluidigm 1.96                        |         12 |       8 |               1 |                  100 | 109xxxxxxx        |                      2
             * 4 | Fluidigm 4.96                        |         12 |       8 |               4 |                  100 | 110xxxxxxx        |                      2
             * 5 | Fluidigm 8.96                        |         12 |       8 |               8 |                  100 | 112xxxxxxx        |                      2
             * 6 | Fluidigm DC10 1.96                   |         12 |       8 |               1 |                  100 | 122xxxxxxx        |                      2
             * 7 | Greiner DC                           |         12 |       8 |               1 |                 2107 | 441310xxxxxx      |                      1
             * 8 | STRUBI Capillary Plates              |         12 |       8 |               1 |                  100 | 441311xxxxxx      |                      1
             * 9 | Zurich Capillary Plates              |          3 |      16 |              19 |                  100 | 441312xxxxxx      |                      2
             */

            // Map into an array of IPlateType[]
            global::OPPF.Integrations.ImagerLink.PlateType[] iptArray = new global::OPPF.Integrations.ImagerLink.PlateType[9];
            int i = 0;

            iptArray[i] = new global::OPPF.Integrations.ImagerLink.PlateType();
            iptArray[i].SetID("1");
            iptArray[i].SetName("Greiner, central shelf only");
            iptArray[i].SetNumColumns(12);
            iptArray[i].SetNumDrops(1);
            iptArray[i].SetNumRows(8);
            i++;

            iptArray[i] = new global::OPPF.Integrations.ImagerLink.PlateType();
            iptArray[i].SetID("2");
            iptArray[i].SetName("OPPF Full Greiner, all three shelves");
            iptArray[i].SetNumColumns(12);
            iptArray[i].SetNumDrops(3);
            iptArray[i].SetNumRows(8);
            i++;

            iptArray[i] = new global::OPPF.Integrations.ImagerLink.PlateType();
            iptArray[i].SetID("3");
            iptArray[i].SetName("Fluidigm 1.96");
            iptArray[i].SetNumColumns(12);
            iptArray[i].SetNumDrops(1);
            iptArray[i].SetNumRows(8);
            i++;

            iptArray[i] = new global::OPPF.Integrations.ImagerLink.PlateType();
            iptArray[i].SetID("4");
            iptArray[i].SetName("Fluidigm 4.96");
            iptArray[i].SetNumColumns(12);
            iptArray[i].SetNumDrops(4);
            iptArray[i].SetNumRows(8);
            i++;

            iptArray[i] = new global::OPPF.Integrations.ImagerLink.PlateType();
            iptArray[i].SetID("5");
            iptArray[i].SetName("Fluidigm 8.96");
            iptArray[i].SetNumColumns(12);
            iptArray[i].SetNumDrops(8);
            iptArray[i].SetNumRows(8);
            i++;

            iptArray[i] = new global::OPPF.Integrations.ImagerLink.PlateType();
            iptArray[i].SetID("6");
            iptArray[i].SetName("Fluidigm DC10 1.96");
            iptArray[i].SetNumColumns(12);
            iptArray[i].SetNumDrops(2);
            iptArray[i].SetNumRows(8);
            i++;

            iptArray[i] = new global::OPPF.Integrations.ImagerLink.PlateType();
            iptArray[i].SetID("7");
            iptArray[i].SetName("Greiner DC");
            iptArray[i].SetNumColumns(12);
            iptArray[i].SetNumDrops(1);
            iptArray[i].SetNumRows(8);
            i++;

            iptArray[i] = new global::OPPF.Integrations.ImagerLink.PlateType();
            iptArray[i].SetID("8");
            iptArray[i].SetName("STRUBI Capillary Plates");
            iptArray[i].SetNumColumns(12);
            iptArray[i].SetNumDrops(1);
            iptArray[i].SetNumRows(8);
            i++;

            iptArray[i] = new global::OPPF.Integrations.ImagerLink.PlateType();
            iptArray[i].SetID("9");
            iptArray[i].SetName("Zurich Capillary Plates");
            iptArray[i].SetNumColumns(3);
            iptArray[i].SetNumDrops(19);
            iptArray[i].SetNumRows(16);
            i++;

            // Copy into the cache and update cache expiry
            _plateTypes             = iptArray;
            _plateTypesCacheExpires = System.DateTime.Now.Ticks + _cacheLifetime;

            _log.Debug("GetPlateTypes() using fresh response");
            // Return the array of IPlateType[]
            return(_plateTypes);
        }
        /// <summary>
        /// Retrieve all the plate types. The list of PlateTypes is cached for _cacheLifetime min.
        /// </summary>
        /// <param name="robot">The robot to find the plate types for.</param>
        /// <returns>An array of plate types, or null if there are none.</returns>
        public IPlateType[] GetPlateTypes(IRobot robot)
        {
            // Check arguments - do it up front to avoid possible inconsistencies later
            if (robot == null)
            {
                throw new System.NullReferenceException("robot must not be null");
            }

            // Log the call
            if (_log.IsDebugEnabled)
            {
                string msg = "Called " + this + ".GetPlateTypes(robot=" + robot.ToString() + ")";
                _log.Debug(msg);
            }

            // Return cached values if appropriate
            if ((_plateTypes != null) && (System.DateTime.Now.Ticks <= _plateTypesCacheExpires))
            {
                _log.Debug("GetPlateTypes() using cached response");
                return(_plateTypes);
            }

            _log.Debug("GetPlateTypes() refreshing cache");

            // Sychronize this block as we are interacting with the cache
            lock (_plateTypesLock)
            {
                try
                {
                    // Create and populate the request object
                    getPlateTypes request = new getPlateTypes();
                    request.robot = global::OPPF.Utilities.RobotUtils.createProxy(robot);

                    // Make the web service call
                    WSPlate wsPlate = new WSPlate();

                    // New stuff
                    wsPlate.Url = "http://localhost:8080/xtalpims-ws/services/WSPlate.WSPlateSOAP12port_http/";
                    ServicePointManager.Expect100Continue = false;
                    wsPlate.Credentials = new NetworkCredential("jon", "test123");
                    // End new stuff

                    getPlateTypesResponse response = wsPlate.getPlateTypes(request);

                    // Get the array of proxy PlateType[]
                    global::OPPF.Proxies.PlateType[] pptArray = response.wrapper;

                    // Map into an array of IPlateType[]
                    global::OPPF.Integrations.ImagerLink.PlateType[] iptArray = new global::OPPF.Integrations.ImagerLink.PlateType[pptArray.Length];
                    int i = 0;
                    foreach (global::OPPF.Proxies.PlateType ppt in pptArray)
                    {
                        iptArray[i] = new global::OPPF.Integrations.ImagerLink.PlateType();
                        iptArray[i].SetID(ppt.iD);
                        iptArray[i].SetName(ppt.name);
                        iptArray[i].SetNumColumns(ppt.numColumns);
                        iptArray[i].SetNumDrops(ppt.numDrops);
                        iptArray[i].SetNumRows(ppt.numRows);
                        i++;
                    }

                    // Copy into the cache and update cache expiry
                    _plateTypes             = iptArray;
                    _plateTypesCacheExpires = System.DateTime.Now.Ticks + _cacheLifetime;

                    _log.Debug("GetPlateTypes() using fresh response");
                }
                catch (Exception e)
                {
                    // Log it
                    string msg = "WSPlate.getPlateTypes threw " + e.GetType() + ": " + e.Message + " for robot \"" + robot.Name + "\" - returning null";
                    msg = msg + WSPlateFactory.SoapExceptionToString(e);
                    _log.Error(msg, e);

                    // Don't rethrow - return cache (which might be null) - don't want to stop imaging
                }
            }

            // Return the array of IPlateType[]
            return(_plateTypes);
        }