Cellario: Running an ODTC without closing the lid

Hey all, any Cellario expert users?

We have a need to run an ODTC controlled by Cellario without closing the lid on the ODTC. I see there’s a “Close lid on Method” in the connection parameter, but this doesn’t appear to have any effect on whether the lid gets closed before running a method.

I also can’t find anything in the Inheco method builder software with regards to if/when the lid of the ODTC gets closed. This much be possible though, right?

Hi John, are you sure the ODTC can even run a method with the lid open? if the ODTC can run a method with the lid open, then Cellario could probably accommodate this. if your device driver on your system does not accommodate this functionality, which i’m pretty sure it does not, then i’d contact support at HighRes. someone will be able to work with you to modify your device driver.

or there are other devices that perform temperature ramping that do not have lids/covers. your system could have those integrated. probably cheaper and more beneficial to have other devices integrated, then your ODTC isn’t tied up.

Let me know if you have any other questions!
Justin

You can,

@jnecr I think when you create the method in the device manager, you can leave the lid open. Do you have device manager documentation? I’m happy to email the docs if you send me your email via PM.

Will PM you, I do not have the device documentation.

I do have an HPAC that I can put on our Bravo that is also integrated, but using the ODTC will be more convenient for several reasons. We’ll also by incubating a plate at temp for 15-20 mins which means if I do this on the Bravo it will be occupied during that time and won’t be able to process any other plates. Meanwhile we have four ODTCs on the system, so a lot of available resources there.

We’re pretty close to getting this working with a C# script, but it seems overly complicated for what I’m trying to do. I may reach out to HRB and see if their driver just doesn’t support running the ODTC with the lid open or if I’m missing something.

1 Like

Skimming the docs, the ODTC driver looks like it has a “CloseDoor” operation , so that is always automatically called after a move. I could be wrong, but I believe that is ingrained in the scheduling logic and not configurable. A script that is set to execute “After Move” will hold until the “CloseDoor” operation finishes though. Assuming you can design an ODTC method that runs with the door open, calling “OpenDoor” in an “After Move” script might be what you have to do to get around this.

Thanks Dennis. Our driver does allow for simple “Set Temp” commands. So our plan is to have a Cellario C# script that sets the temp and then moves the plate to the ODTC without actually kicking off an ODTC method. We’re pretty close, just a few little things to work out. I’ll post our script for others to critique when we’ve got something working.

2 Likes

OK, we finally have something working. Testing has been slow since we have a lot of actual production runs going on. But today was slow for some reason. :slight_smile:

The only way I could get this to work consistently is splitting it up into multiple scripts, one for each discrete step. This isn’t necessarily bad, since it allows us to reuse these scripts in different ways. The other thing that I realized I needed to add were wait steps, I believe this could be achieved by actually polling the resources for their status and not continuing until they reported the command as complete, but I’m not sure I know how to do that.

I’ll also add, that I am by no means a C# expert, so I’m totally open to feedback on how to make these scripts better. Flexibility would also be desired, like not always using ODTC 1.1, we have 4 ODTCs on this system, if we are running this protocol while another protocol runs we’ll need to be very careful to make sure ODTC 1.1 is available.

Open the ODTC and start heating the block/lid:

using System;
using System.Linq;
using HRB.Cellario.Scripting.API;


namespace HeatBlock.Scripting
{
    public class HeatBlock : AbstractScript
    {
        public override void AllocateResources(IScriptingApiAllocation api)
        {
            api.Resources["ODTC 1.1"].Allocate();
            api.Messaging.WriteDiagnostic(ScriptLogLevel.Normal, "allocate done"); //comment
        }

        public override void Execute(IScriptingApi api)
        {

            //open ODTC
            api.Resources["ODTC 1.1"].Operations["OpenDoor"].Execute();
            api.Messaging.WriteDiagnostic(ScriptLogLevel.Normal, "Start Wait");
            System.Threading.Thread.Sleep(15000); // wait 15 seconds
            api.Messaging.WriteDiagnostic(ScriptLogLevel.Normal, "End Wait");
            
            //set ODTC temp
            api.Resources["ODTC 1.1"].Operations["Set Temperature"].OperationParameters["Block Temperature"] = 45;
            api.Messaging.WriteDiagnostic(ScriptLogLevel.Normal, "set block temp done"); //comment
            api.Resources["ODTC 1.1"].Operations["Set Temperature"].OperationParameters["Lid Temperature"] = 45;
            api.Resources["ODTC 1.1"].Operations["Set Temperature"].OperationParameters["Block Temperature Threshold"] = 5;
            api.Resources["ODTC 1.1"].Operations["Set Temperature"].OperationParameters["Lid Temperature Threshold"] = 10;
            api.Resources["ODTC 1.1"].Operations["Set Temperature"].OperationParameters["Blocking"] = false;
            api.Messaging.WriteDiagnostic(ScriptLogLevel.Normal, "set lid temp done"); //comment
           
            api.Resources["ODTC 1.1"].Operations["Set Temperature"].Execute();
            api.Messaging.WriteDiagnostic(ScriptLogLevel.Normal, "set temp execute done"); //comment

            api.Messaging.WriteDiagnostic(ScriptLogLevel.Normal, "Start Wait");
            System.Threading.Thread.Sleep(15000); // wait 15 seconds
            api.Messaging.WriteDiagnostic(ScriptLogLevel.Normal, "End Wait");
        }

        public override void ReleaseResources(IScriptingApiPostExecute api)
        {
            api.Resources["ODTC 1.1"].Release();
        }
    }
}

Moving the plate:

using System;
using System.Linq;
using HRB.Cellario.Scripting.API;


namespace Customer.Scripting
{
    public class MovePlate : AbstractScript
    {
        /// <summary>
        /// Method called ahead of execution to optionally allocate resources.
        /// </summary>
        /// <param name="api">Use to claim device resources that will be used in the script.</param>
        public override void AllocateResources(IScriptingApiAllocation api)
        {
            api.Resources["Robot 1.1"].Allocate();
        }

        /// <summary>
        /// Method called during Cellario protocol execution when a sample arrives at the scripting step.
        /// </summary>
        /// <remarks>Executes synchronously with the run scheduler. Device operation results not available
        /// until <see cref="ReleaseResources"/> method is called.</remarks>
        /// <param name="api">Access to properties and methods for interacting with the Cellario run-time scheduler, 
        /// samples, resources and devices operations.</param>
        public override void Execute(IScriptingApi api)
        {
            api.Resources["Robot 1.1"].Operations["Move"].OperationParameters["Source"] = 33;
            api.Resources["Robot 1.1"].Operations["Move"].OperationParameters["Destination"] = 19;
            api.Resources["Robot 1.1"].Operations["Move"].OperationParameters["Labware"] = "PCR Plate";
            api.Resources["Robot 1.1"].Operations["Move"].OperationParameters["Robot Speed"] = 70;
            api.Resources["Robot 1.1"].Operations["Move"].OperationParameters["Block Height"] = 17.5;
            api.Resources["Robot 1.1"].Operations["Move"].OperationParameters["Thickness"] = 15.1;

            api.Resources["Robot 1.1"].Operations["Move"].Execute();
            api.Messaging.WriteDiagnostic(ScriptLogLevel.Normal, "Start Wait");
            System.Threading.Thread.Sleep(20000); // wait 20 seconds
            api.Messaging.WriteDiagnostic(ScriptLogLevel.Normal, "End Wait");
        }

        /// <summary>
        /// Method called during Cellario protocol execution when any device operations for the sample are complete,
        /// allowing access to their results.
        /// </summary>
        /// <remarks>If a resource is allocated but not released, it will remain unavailable to the Cellario run-time scheduler.</remarks>
        /// <param name="api">Access to properties and methods for interacting with the Cellario run-time scheduler, 
        /// samples, resources and device operations, including releasing allocated resources.</param>
        public override void ReleaseResources(IScriptingApiPostExecute api)
        {
            api.Resources["Robot 1.1"].Release();
        }
    }
}

A script to “incubate” for an amount of time as entered when creating the order:

using System;
using System.Linq;
using HRB.Cellario.Scripting.API;

namespace Customer.Scripting
{
    public class ScriptedWait : AbstractScript
    {
        /// <summary>
        /// Method called ahead of execution to optionally allocate resources.
        /// </summary>
        /// <param name="api">Use to claim device resources that will be used in the script.</param>
        public override void AllocateResources(IScriptingApiAllocation api)
        {
            // TODO - implement or remove if not used
            // e.g. api.Resources["resource name"].Allocate();
        }

        /// <summary>
        /// Method called during Cellario protocol execution when a sample arrives at the scripting step.
        /// </summary>
        /// <remarks>Executes synchronously with the run scheduler. Device operation results not available
        /// until <see cref="ReleaseResources"/> method is called.</remarks>
        /// <param name="api">Access to properties and methods for interacting with the Cellario run-time scheduler, 
        /// samples, resources and devices operations.</param>
        public override void Execute(IScriptingApi api)
        {
            var parameters = api.CurrentRun.RunOrderParameters.Last();
            int iTime;
            iTime = Int32.Parse(parameters.ParameterValue) * 1000;
        
            //var iTime = api.CurrentRun.Protocol.Parameters.
            api.Messaging.WriteDiagnostic(ScriptLogLevel.Normal, "Start Incubate for " + iTime + " milliseconds");
            System.Threading.Thread.Sleep(iTime); // wait
            api.Messaging.WriteDiagnostic(ScriptLogLevel.Normal, "End Incubate");
        }

        /// <summary>
        /// Method called during Cellario protocol execution when any device operations for the sample are complete,
        /// allowing access to their results.
        /// </summary>
        /// <remarks>If a resource is allocated but not released, it will remain unavailable to the Cellario run-time scheduler.</remarks>
        /// <param name="api">Access to properties and methods for interacting with the Cellario run-time scheduler, 
        /// samples, resources and device operations, including releasing allocated resources.</param>
        public override void ReleaseResources(IScriptingApiPostExecute api)
        {
            // TODO  implement or remove if not used
            // e.g. var result = api.Resources["resource name"].Operations["operation name"].Execute();
            // e.g. api.Resources["resource name"].Release();
        }
    }
}

Move the plate again using the same script as above, and then reset the ODTC and close the door:

using System;
using System.Linq;
using HRB.Cellario.Scripting.API;

// TODO - customize namespace and class name
namespace Customer.Scripting
{
    public class ResetODTC : AbstractScript
    {
        /// <summary>
        /// Method called ahead of execution to optionally allocate resources.
        /// </summary>
        /// <param name="api">Use to claim device resources that will be used in the script.</param>
        public override void AllocateResources(IScriptingApiAllocation api)
        {
            // TODO - implement or remove if not used
            api.Resources["ODTC 1.1"].Allocate();
        }

        /// <summary>
        /// Method called during Cellario protocol execution when a sample arrives at the scripting step.
        /// </summary>
        /// <remarks>Executes synchronously with the run scheduler. Device operation results not available
        /// until <see cref="ReleaseResources"/> method is called.</remarks>
        /// <param name="api">Access to properties and methods for interacting with the Cellario run-time scheduler, 
        /// samples, resources and devices operations.</param>
        public override void Execute(IScriptingApi api)
        {
            api.Resources["ODTC 1.1"].Operations["Reset"].Execute();
            api.Messaging.WriteDiagnostic(ScriptLogLevel.Normal, "Start Wait");
            System.Threading.Thread.Sleep(10000); // wait 10 seconds
            api.Messaging.WriteDiagnostic(ScriptLogLevel.Normal, "End Wait");
            api.Resources["ODTC 1.1"].Operations["CloseDoor"].Execute();
            api.Messaging.WriteDiagnostic(ScriptLogLevel.Normal, "Start Wait");
            System.Threading.Thread.Sleep(10000); // wait 10 seconds
            api.Messaging.WriteDiagnostic(ScriptLogLevel.Normal, "End Wait");
        }

        /// <summary>
        /// Method called during Cellario protocol execution when any device operations for the sample are complete,
        /// allowing access to their results.
        /// </summary>
        /// <remarks>If a resource is allocated but not released, it will remain unavailable to the Cellario run-time scheduler.</remarks>
        /// <param name="api">Access to properties and methods for interacting with the Cellario run-time scheduler, 
        /// samples, resources and device operations, including releasing allocated resources.</param>
        public override void ReleaseResources(IScriptingApiPostExecute api)
        {
            // TODO  implement or remove if not used
            // e.g. var result = api.Resources["resource name"].Operations["operation name"].Execute();
            api.Resources["ODTC 1.1"].Release();
        }
    }
}
2 Likes

“OpenDoor” and “CloseDoor” are operations tied to the ODTC resource type. Cellario will always call them when moving to and from the device. I don’t believe there’s a way to have one without the other at a resource level.

If you are ok with the door closing and then opening you could have an after move script that opens the ODTC door via the OpenDoor Operation. Based on your solution it seems like you don’t want the door to ever close while labware is placed.

The cleanest way to have this done would be to reach out to HRB support looking to add an operation parameter that would allow skipping the Close Door call. I don’t think that this has come up before since the existing parameters are mostly ensuring that the door does close and retrying if it failed to do so. This new parameter would allow you to create orders without additional scripting.

If you are properly allocating and releasing resources you should be ok to run other orders alongside this one. You may run into deadlocking issues depending on protocol design. For selecting different ODTC’s I would make the resource name a variable that reads from a protocol or runorder parameter, then you can select which ODTC to use when creating your order. There should be example scripts in your Cellario instance showing how to reference those parameters.

1 Like