// default setup agent func BasicSetupAgent(request *LHRequest, params *liquidhandling.LHProperties) *LHRequest { // this is quite tricky and requires extensive interaction with the liquid handling // parameters // the principal question is how to define constraints on the system // I think this needs to remain tbd for now // instead we can rely on the preference system I already use plate_lookup := make(map[string]string, 5) tip_lookup := make([]*wtype.LHTipbox, 0, 5) tip_preferences := params.Tip_preferences input_preferences := params.Input_preferences output_preferences := params.Output_preferences // how do we set the below? // we don't know how many tips we need until we generate // instructions; ditto input or output plates until we've done layout // input plates input_plates := request.Input_plates // output plates output_plates := request.Output_plates // tips tips := request.Tips // we put tips on first setup := request.Setup if len(setup) == 0 { setup = wtype.NewLHSetup() } for _, tb := range tips { // get the first available position from the preferences pos := get_first_available_preference(tip_preferences, setup) if pos == -1 { RaiseError("No positions left for tipbox") } position := "position_" + strconv.Itoa(pos) setup[position] = tb plate_lookup[tb.ID] = position tip_lookup = append(tip_lookup, tb) } setup["tip_lookup"] = tip_lookup // this logic may not transfer well but I expect that outputs are more constrained // than inputs for the simple reason that most output takes place to single wells // while input takes place from reservoirs // outputs for _, p := range output_plates { pos := get_first_available_preference(output_preferences, setup) if pos == -1 { RaiseError("No positions left for output") } position := "position_" + strconv.Itoa(pos) setup[position] = p plate_lookup[p.ID] = position params.AddPlate(position, p) } // inputs for _, p := range input_plates { pos := get_first_available_preference(input_preferences, setup) if pos == -1 { RaiseError("No positions left for input") } position := "position_" + strconv.Itoa(pos) setup[position] = p plate_lookup[p.ID] = position params.AddPlate(position, p) } request.Setup = setup request.Plate_lookup = plate_lookup return request }
func AdvancedExecutionPlanner(request *LHRequest, parameters *liquidhandling.LHProperties) *LHRequest { // in the first instance we assume this is done component-wise // we also need to identify dependencies, i.e. if certain components // are only available after other actions // this will only work if the components to be added are to go in the same order // get the layout groups minorlayoutgroups := request.Output_minor_group_layouts ass := request.Output_assignments inass := request.Input_assignments output_solutions := request.Output_solutions input_plates := request.Input_plates output_plate_layout := request.Output_plate_layout plate_lookup := request.Plate_lookup // highly inelegant... we should swap Tip Box Setup // around to take place after, then this whole thing // is a non-issue tt := make([]*wtype.LHTip, 1) tt[0] = request.Tip_Type.Tiptype parameters.Tips = tt instructions := liquidhandling.NewRobotInstructionSet(nil) // need to deal with solutions order := request.Input_order for _, name := range order { //fmt.Println(name) // cmpa holds a list of inputs required per destination // i.e. if destination X requires 15 ul of h2o this will be listed separately // at this point all of the requests must be for volumes, // we need a mapping from these to where they belong // do we? /* cmpa:=value.([]map[string]interface{}) cmp_map:=make(map[string]interface{}, len(cmpa)) for _,cmp:=range cmpa{ srcid:=cmp.Srcid cmp_map[srcid]=cmp } */ for n, g := range minorlayoutgroups { grp := []string(g) // get the group assignment string assignment := ass[n] // the assignment has the format plateID:row:column:incrow:inccol // where inc defines how the next one is to be calculated // e.g. {GUID}:A:1:1:0 // {GUID}:A:1:0:1 asstx := strings.Split(assignment, ":") plate := asstx[0] toplatenum := wutil.ParseInt(plate) row := wutil.AlphaToNum(asstx[1]) col := wutil.ParseInt(asstx[2]) incrow := wutil.ParseInt(asstx[3]) inccol := wutil.ParseInt(asstx[4]) whats := make([]string, len(grp)) pltfrom := make([]string, len(grp)) pltto := make([]string, len(grp)) plttypefrom := make([]string, len(grp)) plttypeto := make([]string, len(grp)) wellfrom := make([]string, len(grp)) wellto := make([]string, len(grp)) vols := make([]*wunit.Volume, len(grp)) fvols := make([]*wunit.Volume, len(grp)) tvols := make([]*wunit.Volume, len(grp)) for i, solID := range grp { sol := output_solutions[solID] // we need to get the relevant component out smpl := get_aggregate_component(sol, name) // we need to know where this component was assigned to inassignmentar := []string(inass[name]) inassignment, ok := get_assignment(inassignmentar, &input_plates, smpl.Vol) if !ok { wutil.Error(errors.New(fmt.Sprintf("No input assignment for %s with vol %-4.1f", name, smpl.Vol))) } inasstx := strings.Split(inassignment, ":") inplt := inasstx[0] inrow := string(inasstx[1]) incol := wutil.ParseInt(inasstx[2]) // we can fill the structure now whats[i] = name pltfrom[i] = plate_lookup[string(inplt)] pltto[i] = plate_lookup[output_plate_layout[toplatenum]] wellfrom[i] = inrow + strconv.Itoa(incol) wellto[i] = wutil.NumToAlpha(row) + strconv.Itoa(col) v := wunit.NewVolume(smpl.Vol, smpl.Vunit) v2 := wunit.NewVolume(0.0, "ul") vols[i] = &v // TODO Get the proper volumes here fvols[i] = &v2 tvols[i] = &v2 row += incrow col += inccol } ins := liquidhandling.NewTransferInstruction(whats, pltfrom, pltto, wellfrom, wellto, plttypefrom, plttypeto, vols, fvols, tvols /*, parameters.Cnfvol*/) instructions.Add(ins) } } inx := instructions.Generate(request.Policies, parameters) instrx := make([]liquidhandling.TerminalRobotInstruction, len(inx)) for i := 0; i < len(inx); i++ { instrx[i] = inx[i].(liquidhandling.TerminalRobotInstruction) } request.Instructions = instrx return request }