/// <summary> /// Executes a "buy" event /// </summary> /// <param name="AnimalInfo"></param> /// <returns></returns> protected int Buy(TPurchaseInfo AnimalInfo) { TAnimalParamSet aGenotype; TAnimalGroup NewGroup; double fBodyCondition; double fLiveWeight; double fLowBaseWeight = 0.0; double fHighBaseWeight = 0.0; TAnimalList WeanList; int PaddNo; int result = 0; if (AnimalInfo.Number > 0) { aGenotype = getGenotype(AnimalInfo.sGenotype); if (AnimalInfo.LiveWt > 0.0) fLiveWeight = AnimalInfo.LiveWt; else { fLiveWeight = GrowthCurve(AnimalInfo.AgeDays, AnimalInfo.Repro, aGenotype); if (AnimalInfo.fCondScore > 0.0) fLiveWeight = fLiveWeight * TAnimalParamSet.CondScore2Condition(AnimalInfo.fCondScore); if (aGenotype.Animal == GrazType.AnimalType.Sheep) fLiveWeight = fLiveWeight + AnimalInfo.GFW; } // Construct a new group of animals. NewGroup = new TAnimalGroup(aGenotype, AnimalInfo.Repro, // Repro should be Empty, Castrated or AnimalInfo.Number, // Male; pregnancy is handled with the AnimalInfo.AgeDays, // Preg field. fLiveWeight, AnimalInfo.GFW, RandFactory); if ((AnimalInfo.fCondScore > 0.0) && (AnimalInfo.LiveWt > 0.0)) // Adjust the condition score if it has { // been given fBodyCondition = TAnimalParamSet.CondScore2Condition(AnimalInfo.fCondScore); NewGroup.WeightRangeForCond(AnimalInfo.Repro, AnimalInfo.AgeDays, fBodyCondition, NewGroup.Genotype, ref fLowBaseWeight, ref fHighBaseWeight); if ((NewGroup.BaseWeight >= fLowBaseWeight) && (NewGroup.BaseWeight <= fHighBaseWeight)) NewGroup.setConditionAtWeight(fBodyCondition); else { NewGroup = null; throw new Exception("Purchased animals with condition score " + AnimalInfo.fCondScore.ToString() + "\n" + " must have a base weight in the range " + fLowBaseWeight.ToString() + "-" + fHighBaseWeight.ToString() + " kg"); } } if (NewGroup.ReproState == GrazType.ReproType.Empty) { if (AnimalInfo.sMatedTo != "") // Use TAnimalGroup's property interface NewGroup.MatedTo = getGenotype(AnimalInfo.sMatedTo); // to set up pregnancy and lactation. NewGroup.Pregnancy = AnimalInfo.Preg; NewGroup.Lactation = AnimalInfo.Lact; if ((NewGroup.Animal == GrazType.AnimalType.Cattle) && (AnimalInfo.Lact > 0) && (AnimalInfo.NYoung == 0)) // NYoung denotes the number of { // *suckling* young in lactating cows, WeanList = null; // which isn't quite the same as the NewGroup.Wean(true, true, ref WeanList, ref WeanList); // YoungNo property WeanList = null; ; } else if (AnimalInfo.NYoung > 0) { // if the animals are pregnant then they need feotuses if (NewGroup.Pregnancy > 0) { if ((AnimalInfo.Lact > 0) && (NewGroup.Animal == GrazType.AnimalType.Cattle)) { NewGroup.NoOffspring = 1; NewGroup.NoFoetuses = Math.Min(2, Math.Max(0, AnimalInfo.NYoung - 1)); } else { NewGroup.NoFoetuses = Math.Min(3, AnimalInfo.NYoung); // recalculates livewt } } else NewGroup.NoOffspring = AnimalInfo.NYoung; } if (NewGroup.Young != null) // Lamb/calf weights and lamb fleece { // weights are optional. if (bIsGiven(AnimalInfo.YoungWt)) NewGroup.Young.LiveWeight = AnimalInfo.YoungWt; if (bIsGiven(AnimalInfo.YoungGFW)) NewGroup.Young.FleeceCutWeight = AnimalInfo.YoungGFW; } } //if (ReproState = Empty) PaddNo = 0; // Newly bought animals have tag # zero while ((PaddNo < Paddocks.Count()) && (Paddocks.byIndex(PaddNo).sName == "")) // and go in the first named paddock. PaddNo++; if (PaddNo >= Paddocks.Count()) PaddNo = 0; result = Add(NewGroup, Paddocks.byIndex(PaddNo), 0, 0); } // if AnimalInfo.Number > 0 return result; }
/// <summary> /// Manage the replacement of adults by purchasing or ageing the existing stock. /// </summary> /// <param name="currentDay"></param> /// <param name="curEnt"></param> protected void manageReplacement(int currentDay, TEnterpriseInfo curEnt) { double area; int numToBuy; int totalStock; int g, groups; int groupIdx; TPurchaseInfo AnimalInfo = new TPurchaseInfo(); int yrs; TAnimalParamSet genoprms; TEnterpriseInfo.TStockEnterprise stockEnt; if (curEnt.ReplacementDay == currentDay) { area = calcStockArea(curEnt); if (area > 0) { totalStock = 0; g = 1; groups = Count(); while (g <= groups) //for each group { if (curEnt.ContainsTag(getTag(g))) //if this group belongs to this ent totalStock = totalStock + At(g).NoAnimals; g++; } numToBuy = Convert.ToInt32(Math.Truncate(curEnt.StockRateFemale * area) - totalStock); //calc how many to purchase to maintain stocking rate if (!curEnt.getPurchase(1)) //if self replacing { // tag enough young ewes as replacements // sell excess young ewes } else { stockEnt = curEnt.EntTypeFromName(curEnt.EntClass); genoprms = getGenotype(curEnt.BaseGenoType); switch (stockEnt) { case TEnterpriseInfo.TStockEnterprise.entLamb: case TEnterpriseInfo.TStockEnterprise.entWether: case TEnterpriseInfo.TStockEnterprise.entSteer: AnimalInfo.sGenotype = curEnt.BaseGenoType; AnimalInfo.Number = numToBuy; AnimalInfo.Repro = GrazType.ReproType.Castrated; AnimalInfo.AgeDays = Convert.ToInt32(Math.Truncate(MONTH2DAY * curEnt.ReplaceAge + 0.5)); AnimalInfo.LiveWt = curEnt.ReplaceWeight; AnimalInfo.GFW = 0; AnimalInfo.fCondScore = TAnimalParamSet.Condition2CondScore(curEnt.ReplaceCond); AnimalInfo.sMatedTo = ""; AnimalInfo.Preg = 0; AnimalInfo.Lact = 0; AnimalInfo.NYoung = 0; AnimalInfo.NYoung = 0; AnimalInfo.YoungWt = 0; AnimalInfo.YoungGFW = 0; if ((stockEnt == TEnterpriseInfo.TStockEnterprise.entWether) || (stockEnt == TEnterpriseInfo.TStockEnterprise.entLamb)) { AnimalInfo.GFW = genoprms.PotentialGFW * DaysFromDOY(curEnt.ShearingDate, currentDay) / 365.0; } if (AnimalInfo.Number > 0) { groupIdx = Buy(AnimalInfo); yrs = AnimalInfo.AgeDays / 365; TagGroup(curEnt, groupIdx, 1); //tag the group // {TODO: before drafting, a request of forages should be done} DraftToOpenPaddocks(curEnt, area); /* //find the first paddock to be stocked for this enterprise p = 0; while (p <= paddocks.Count - 1) do begin if curEnt.StockedPaddock[p+1] //if paddock to be stocked begin InPadd[groupIdx] = paddocks.byIndex(p).sName; //move into correct paddock p = paddocks.Count; //terminate loop end; inc(p); end; //if none found default to the first paddock if p = paddocks.Count InPadd[groupIdx] = paddocks.byIndex(0).sName; */ } break; } } } } }
/// <summary> /// The main stock management function that handles a number of events. /// </summary> /// <param name="Model"></param> /// <param name="stockEvent">The event parameters</param> /// <param name="dtToday"></param> /// <param name="fLatitude"></param> public void doStockManagement(TStockList Model, IStockEvent stockEvent, int dtToday = 0, double fLatitude = -35.0) { TCohortsInfo CohortsInfo = new TCohortsInfo(); TPurchaseInfo PurchaseInfo = new TPurchaseInfo(); List<string> sClosed; string sParam; int iParam1; int iParam3; double fValue; int iTag; int iGroups; if (stockEvent != null) { if (stockEvent.GetType() == typeof(TStockAdd)) // add_animals { TStockAdd stockInfo = (TStockAdd)stockEvent; CohortsInfo.sGenotype = stockInfo.genotype; CohortsInfo.iNumber = Math.Max(0, stockInfo.number); if (!ParseRepro(stockInfo.sex, ref CohortsInfo.ReproClass)) throw new Exception("Event ADD does not support sex='" + stockInfo.sex + "'"); if (dtToday > 0) CohortsInfo.iAgeOffsetDays = DaysFromDOY365(stockInfo.birth_day, dtToday); else CohortsInfo.iAgeOffsetDays = 0; CohortsInfo.iMinYears = stockInfo.min_years; CohortsInfo.iMaxYears = stockInfo.max_years; CohortsInfo.fMeanLiveWt = stockInfo.mean_weight; CohortsInfo.fCondScore = stockInfo.cond_score; CohortsInfo.fMeanGFW = stockInfo.mean_fleece_wt; if (dtToday > 0) CohortsInfo.iFleeceDays = DaysFromDOY365(stockInfo.shear_day, dtToday); else CohortsInfo.iFleeceDays = 0; CohortsInfo.sMatedTo = stockInfo.mated_to; CohortsInfo.iDaysPreg = stockInfo.pregnant; CohortsInfo.fFoetuses = stockInfo.foetuses; CohortsInfo.iDaysLact = stockInfo.lactating; CohortsInfo.fOffspring = stockInfo.offspring; CohortsInfo.fOffspringWt = stockInfo.young_wt; CohortsInfo.fOffspringCS = stockInfo.young_cond_score; CohortsInfo.fLambGFW = stockInfo.young_fleece_wt; if (CohortsInfo.iNumber > 0) Model.AddCohorts(CohortsInfo, 1 + DaysFromDOY365(1, dtToday), fLatitude, null); } else if (stockEvent.GetType() == typeof(TStockBuy)) { TStockBuy stockInfo = (TStockBuy)stockEvent; PurchaseInfo.sGenotype = stockInfo.genotype; PurchaseInfo.Number = Math.Max(0, stockInfo.number); if (!ParseRepro(stockInfo.sex, ref PurchaseInfo.Repro)) throw new Exception("Event BUY does not support sex='" + stockInfo.sex + "'"); PurchaseInfo.AgeDays = Convert.ToInt32(Math.Round(MONTH2DAY * stockInfo.age)); // Age in months PurchaseInfo.LiveWt = stockInfo.weight; PurchaseInfo.GFW = stockInfo.fleece_wt; PurchaseInfo.fCondScore = stockInfo.cond_score; PurchaseInfo.sMatedTo = stockInfo.mated_to; PurchaseInfo.Preg = stockInfo.pregnant; PurchaseInfo.Lact = stockInfo.lactating; PurchaseInfo.NYoung = stockInfo.no_young; if ((PurchaseInfo.Preg > 0) || (PurchaseInfo.Lact > 0)) PurchaseInfo.NYoung = Math.Max(1, PurchaseInfo.NYoung); PurchaseInfo.YoungWt = stockInfo.young_wt; if ((PurchaseInfo.Lact == 0) || (PurchaseInfo.YoungWt == 0.0)) // Can't use MISSING as default owing PurchaseInfo.YoungWt = StdMath.DMISSING; // to double-to-single conversion PurchaseInfo.YoungGFW = stockInfo.young_fleece_wt; iTag = stockInfo.usetag; if (PurchaseInfo.Number > 0) { Model.Buy(PurchaseInfo); if (iTag > 0) Model.setTag(Model.Count(), iTag); } } //_ buy _ //sell a number from a group of animals else if (stockEvent.GetType() == typeof(TStockSell)) { TStockSell stockInfo = (TStockSell)stockEvent; Model.Sell(stockInfo.group, stockInfo.number); } //sell a number of animals tagged with a specific tag else if (stockEvent.GetType() == typeof(TStockSellTag)) { TStockSellTag stockInfo = (TStockSellTag)stockEvent; Model.SellTag(stockInfo.tag, stockInfo.number); } else if (stockEvent.GetType() == typeof(TStockShear)) { TStockShear stockInfo = (TStockShear)stockEvent; sParam = stockInfo.sub_group.ToLower(); Model.Shear(stockInfo.group, ((sParam == "adults") || (sParam == "both") || (sParam == "")), ((sParam == "lambs") || (sParam == "both"))); } else if (stockEvent.GetType() == typeof(TStockMove)) { TStockMove stockInfo = (TStockMove)stockEvent; iParam1 = stockInfo.group; if ((iParam1 >= 1) && (iParam1 <= Model.Count())) Model.setInPadd(iParam1, stockInfo.paddock); else throw new Exception("Invalid group number in MOVE event"); } else if (stockEvent.GetType() == typeof(TStockJoin)) { TStockJoin stockInfo = (TStockJoin)stockEvent; Model.Join(stockInfo.group, stockInfo.mate_to, stockInfo.mate_days); } else if (stockEvent.GetType() == typeof(TStockCastrate)) { TStockCastrate stockInfo = (TStockCastrate)stockEvent; Model.Castrate(stockInfo.group, stockInfo.number); } else if (stockEvent.GetType() == typeof(TStockWean)) { TStockWean stockInfo = (TStockWean)stockEvent; iParam1 = stockInfo.group; sParam = stockInfo.sex.ToLower(); iParam3 = stockInfo.number; if (sParam == "males") Model.Wean(iParam1, iParam3, false, true); else if (sParam == "females") Model.Wean(iParam1, iParam3, true, false); else if ((sParam == "all") || (sParam == "")) Model.Wean(iParam1, iParam3, true, true); else throw new Exception("Invalid offspring type \"" + sParam + "\" in WEAN event"); } else if (stockEvent.GetType() == typeof(TStockDryoff)) { TStockDryoff stockInfo = (TStockDryoff)stockEvent; Model.DryOff(stockInfo.group, stockInfo.number); } //split off the requested animals from all groups else if (stockEvent.GetType() == typeof(TStockSplitAll)) { TStockSplitAll stockInfo = (TStockSplitAll)stockEvent; iGroups = Model.Count(); //get pre-split count of groups for (iParam1 = 1; iParam1 <= iGroups; iParam1++) { sParam = stockInfo.type.ToLower(); fValue = stockInfo.value; iTag = stockInfo.othertag; if (sParam == "age") Model.SplitAge(iParam1, Convert.ToInt32(Math.Round(fValue))); else if (sParam == "weight") Model.SplitWeight(iParam1, fValue); else if (sParam == "young") Model.SplitYoung(iParam1); else if (sParam == "number") Model.Split(iParam1, Convert.ToInt32(Math.Round(fValue))); else throw new Exception("Stock: invalid keyword (" + sParam + ") in \"split\" event"); if ((iTag > 0) && (Model.Count() > iGroups)) //if a tag for any new group is given Model.setTag(Model.Count(), iTag); } } //split off the requested animals from one group else if (stockEvent.GetType() == typeof(TStockSplit)) { TStockSplit stockInfo = (TStockSplit)stockEvent; iGroups = Model.Count(); //get pre-split count of groups iParam1 = stockInfo.group; sParam = stockInfo.type.ToLower(); fValue = stockInfo.value; iTag = stockInfo.othertag; if ((iParam1 < 1) && (iParam1 > Model.Count())) throw new Exception("Invalid group number in SPLIT event"); else if (sParam == "age") Model.SplitAge(iParam1, Convert.ToInt32(Math.Round(fValue))); else if (sParam == "weight") Model.SplitWeight(iParam1, fValue); else if (sParam == "young") Model.SplitYoung(iParam1); else if (sParam == "number") Model.Split(iParam1, Convert.ToInt32(Math.Round(fValue))); else throw new Exception("Stock: invalid keyword (" + sParam + ") in \"split\" event"); if ((iTag > 0) && (Model.Count() > iGroups)) //if a tag for the new group is given Model.setTag(Model.Count(), iTag); } else if (stockEvent.GetType() == typeof(TStockTag)) { TStockTag stockInfo = (TStockTag)stockEvent; iParam1 = stockInfo.group; if ((iParam1 >= 1) && (iParam1 <= Model.Count())) Model.setTag(iParam1, stockInfo.value); else throw new Exception("Invalid group number in TAG event"); } else if (stockEvent.GetType() == typeof(TStockSort)) { Model.Sort(); } else if (stockEvent.GetType() == typeof(TStockPrioritise)) { TStockPrioritise stockInfo = (TStockPrioritise)stockEvent; iParam1 = stockInfo.group; if ((iParam1 >= 1) && (iParam1 <= Model.Count())) Model.setPriority(iParam1, stockInfo.value); else throw new Exception("Invalid group number in PRIORITISE event"); } else if (stockEvent.GetType() == typeof(TStockDraft)) { TStockDraft stockInfo = (TStockDraft)stockEvent; sClosed = new List<string>(stockInfo.closed); Model.Draft(sClosed); } else throw new Exception("Event not recognised in STOCK"); } }