/// <summary> /// Course correction calculations are Async on worker threads. In order to allow the rest of the game to /// run, the update runs as a state machine and should be polled periodically to check when the full /// correction calculation is done. /// /// A calculation is started with CalculationStart() and then this routine is polled until it returns true. /// /// Results are retrieved with GetCorrection(); /// /// </summary> /// <returns></returns> public bool CalculationUpdate() { switch (calcState) { case CalcState.INITIAL_THREE: if (threadsPending == 0) { Debug.Log(CorrectionsLog()); // results are in, figure out which direction is working and start a new thread to // see if we are not within the desired accuracy if (CorrectionInitialEstimate()) { calcState = CalcState.REFINING; } else { // Correction estimation failed. Use the 0 correction path correctionFinal = correctionData[1]; calcState = CalcState.DONE; } } break; case CalcState.REFINING: if (threadsPending == 0) { Debug.Log(CorrectionsLog()); // TODO: check the result, refine if necessary calcState = CalcState.DO_CALLBACK; Debug.LogFormat("correction={0} gives distance={1} for target={2}", correctionData[0].correction, correctionData[0].distance, targetDistance); correctionFinal = correctionData[0]; } break; case CalcState.CLOSEST_APPROACH: if (threadsPending == 0) { calcState = CalcState.DO_CALLBACK; } break; case CalcState.NOT_STARTED: case CalcState.DONE: case CalcState.DO_CALLBACK: break; default: Debug.LogError("Unsupported state"); break; } if (calcState == CalcState.DO_CALLBACK) { calcCallback(this); calcState = CalcState.DONE; } return(calcState == CalcState.DONE); }
private void CalcResultHandler(double distance, CorrectionData threadArgs) { Debug.LogFormat("thread done: correction={0} distance={1} @ t={2} exec time={3} (ms)", threadArgs.correction, distance, threadArgs.timeAtApproach, threadArgs.execTimeMs); threadCountMutex.WaitOne(); threadsPending--; threadCountMutex.ReleaseMutex(); CalculationUpdate(); }
public void ClosestApproachAsync(CorrectionData correctionData, CalcCallback calcCallback) { correctionData.gravityState.isAsync = true; this.calcCallback = calcCallback; calcState = CalcState.CLOSEST_APPROACH; // setup Mutex threadCountMutex.WaitOne(); threadsPending++; threadCountMutex.ReleaseMutex(); System.Threading.ThreadPool.QueueUserWorkItem( new System.Threading.WaitCallback(CalcCorrectionThread), new object[] { correctionData, CreateUnityAdapter(), (JobResultHandler)CalcResultHandler }); }
//--------------------------------------------------------------------------------------- // Code from: http://blog.yamanyar.com/2015/05/unity-creating-c-thread-with-callback.html //--------------------------------------------------------------------------------------- public void CalcCorrectionThread(object state) { object[] array = state as object[]; ThreadAdapter adapter = array[1] as ThreadAdapter; JobResultHandler callback = array[2] as JobResultHandler; CorrectionData calcData = array[0] as CorrectionData; double distance = ClosestApproach(calcData); //if adapter is not null; callback is also not null. if (adapter != null) { adapter.ExecuteOnUi(delegate { callback(distance, calcData); }); } }
public string CorrectionCalcAsync(double targetDistance, double targetAccuracy, double approachDistance, double maxTime, CalcCallback calcCallback) { this.calcCallback = calcCallback; double[] corrections = { -0.001, 0, 0.001 }; this.targetDistance = targetDistance; this.targetAccuracy = targetAccuracy; threadsPending = 0; calcState = CalcState.NOT_STARTED; string s = ""; GravityEngine ge = GravityEngine.Instance(); correctionData = new CorrectionData[corrections.Length]; int i = 0; foreach (double correction in corrections) { // Run each computation as a dedicated thread correctionData[i] = new CorrectionData(); correctionData[i].gravityState = ge.GetGravityStateCopy(); correctionData[i].approachDistance = approachDistance; correctionData[i].maxPhysTime = maxTime; correctionData[i].correction = correction; threadCountMutex.WaitOne(); threadsPending++; threadCountMutex.ReleaseMutex(); System.Threading.ThreadPool.QueueUserWorkItem( new System.Threading.WaitCallback(CalcCorrectionThread), new object[] { correctionData[i], CreateUnityAdapter(), (JobResultHandler)CalcResultHandler }); i++; } calcState = CalcState.INITIAL_THREE; return(s); }
/// <summary> /// Perform Lunar closest approach and course correction calculations. /// /// ClosestApproach calculations can be done on the main thread or asynch with callback. /// /// Course correction calculations can only be done async with a callback. /// /// Note that the threading APIs assume that a specific Async call will complete before another one /// is started. If multiple parallel computations are required, instantiate additional copies of this /// class. /// /// </summary> /// <param name="targetDistance"></param> /// <returns></returns> public double ClosestApproach(CorrectionData calcData) { long start_ms = System.DateTimeOffset.Now.Millisecond; Vector3 shipPos = calcData.gravityState.GetPhysicsPosition(spaceship); Vector3 moonPos = calcData.gravityState.GetPhysicsPosition(moon); float lastDistance = Vector3.Distance(shipPos, moonPos); float distance = 0f; double end_phys_time = calcData.gravityState.time + calcData.maxPhysTime; searchDt = searchDt_coarse; // correction is applied to the ship velocity purely in the direction the ship is travelling // VERY simplistic approach for first attempt double[] shipVel = new double[] { 0, 0, 0 }; calcData.gravityState.GetVelocityDouble(spaceship, ref shipVel); shipVel[0] *= (1 + calcData.correction); shipVel[1] *= (1 + calcData.correction); shipVel[2] *= (1 + calcData.correction); calcData.gravityState.SetVelocityDouble(spaceship, ref shipVel); // @TODO: dumb implementation: needs refinement // HACK: add a time limit on simulation run time const float TIME_LIMIT_SEC = 20f; float timeEnd_ms = start_ms + TIME_LIMIT_SEC * 1000; while ((System.DateTimeOffset.Now.Millisecond < timeEnd_ms) && (calcData.gravityState.time < end_phys_time)) { calcData.gravityState.Evolve(GravityEngine.Instance(), searchDt); // check if we have passed through min distance shipPos = calcData.gravityState.GetPhysicsPosition(spaceship); moonPos = calcData.gravityState.GetPhysicsPosition(moon); distance = Vector3.Distance(shipPos, moonPos); float delta = distance - lastDistance; // Need to be within approach distance to care (screens out cases where ship is in orbit around // planet and sign change in delta is triggered) if (distance < calcData.approachDistance) { searchDt = searchDt_fine; if (delta > 0f) { break; } } lastDistance = distance; } calcData.distance = distance; calcData.timeAtApproach = calcData.gravityState.time; calcData.execTimeMs = System.DateTimeOffset.Now.Millisecond - start_ms; Debug.Log(string.Format("Closest approach d={0} @ t={1} maxTime={2}", calcData.distance, calcData.timeAtApproach, calcData.maxPhysTime)); if (calcData.gravityState.time > end_phys_time) { calcData.distance = -1; Debug.LogWarning("Physics evolution time exceeded"); } else if (System.DateTimeOffset.Now.Millisecond > timeEnd_ms) { calcData.distance = -2; Debug.LogWarning("Run-time limit exceeded"); } return(calcData.distance); }