private void OnDisplayOnSkillWindow(InputsForFakeWindow valuesToDisplay) { if (DisplayOnSkillWindow != null) { DisplayOnSkillWindow(this, valuesToDisplay); } }
void _DedicatedSee_DisplayOnSkillWindow(object sender, InputsForFakeWindow args) { if (!Dispatcher.CheckAccess()) // CheckAccess returns true if you're on the dispatcher thread { Dispatcher.BeginInvoke(new DedicatedPopUpSee.DisplayOnSkillWindowDelegate(_DedicatedSee_DisplayOnSkillWindow), this, args); return; } // check that the current session matches the session that raised the event. //if (!Object.ReferenceEquals(this, sender)) //{ // return; //} UpdateButtonVisibility(); if (args.StartANewWindow) { double timeSec = 1.0 * args.ExecutionTimeMs / 1000.0; _windowSkillExe = new WindowSkillExecution(args.NameOfSkill.ToString(), timeSec.ToString(), args.Status); _windowSkillExe.Show(); return; } if (args.CloseTheWindow) { if (_windowSkillExe != null) { _windowSkillExe.Close(); } return; } //If code passes here, this mean it was just for a refresh if (_windowSkillExe != null) { if (args.RemainingTimeMs != -1) { double timeSec = 1.0 * args.RemainingTimeMs / 1000.0; _windowSkillExe.changeTime(timeSec.ToString()); _windowSkillExe.changeStatus(args.Status); } else { //Pausing is called _windowSkillExe.changeStatus(args.Status); } } //Refresh buttons UpdateButtonVisibility(); return; }
protected override int ProvideInputDataForSkillExecution(ExecutableSkill SkillToExecute, out InputParams ParametersForTheSkill) { //You can throw an exception, or return an error code is something is weird in the provided Skill => your SEE will go in error state //Note that SkillToExecute is tested before (in mother class), so it will NOT be null, // and SkillToExecute.AmlSkillDescription.Execution.Type is also NOT NULL if (SkillToExecute == null) { ParametersForTheSkill = null; return(-1); } if (string.IsNullOrEmpty(SkillToExecute.AmlSkillDescription.Name)) { //I need the name of the SKill, because I will display it on the Popup Window. //So if the name is not available, I can return a interger!=0 //or I can fire an exception (this way, I can also add a meaningfull error message) ParametersForTheSkill = null; throw new SkillProException("Required SkillToExecute.AmlSkillDescription.Name was NULL or empty"); } //Here, do a big switch case on the Execution's type of the skill. //In our case, since we allways execute the "ProcessWindow" skill (popup), we always have the same input int RandomNumber = _rand.Next(MS_MIN_RANDOM, MS_MAX_RANDOM); InputsForFakeWindow In = new InputsForFakeWindow() { NameOfSkill = SkillToExecute.AmlSkillDescription.Name, ExecutionTimeMs = RandomNumber, RemainingTimeMs = RandomNumber, AlternativePostConditionAvailable = SkillToExecute.AmlSkillDescription.AltPostCondition != null }; switch (SkillToExecute.AmlSkillDescription.Execution.Type) { case "Type1": //In our case, we always have the same input params (because one skill) ParametersForTheSkill = new InputParams(0, (object)In); break; default: //In our case, all the skills will be the "ProcessWindow" (popup) ParametersForTheSkill = new InputParams(0, (object)In); break; } //Return 0 if everything is allright return(0); }
void inputs_Pausing(object sender, EventArgs e) { InputsForFakeWindow argsPause = new InputsForFakeWindow() { StartANewWindow = false, RemainingTimeMs = -1, CloseTheWindow = false, ExecutionTimeMs = -1, NameOfSkill = "", Status = "PAUSED" }; OnDisplayOnSkillWindow(argsPause); }
private OutputParams CloseWindow(InputParams inputs, ref bool SkillIsPausable) { //This is the implementation of the "second" Method provided to the Skills (the "closing method") //Which was more or less designed to cancel/stop/quit an execution //Skill is currently not pausable, and will never be pausable => so let SkillIsPausable set to false SkillIsPausable = false; InputsForFakeWindow value; //First cast the inputs try { value = (InputsForFakeWindow)inputs.Values; } catch (System.InvalidCastException ex) { //If you can't cast the inputs, then there is a special error code for this return(new OutputParams(Skill.ERROR_VALUES_CLOSING_INVALID, null)); } try { //Ask the UI thread to close the window InputsForFakeWindow argsClose = new InputsForFakeWindow() { StartANewWindow = false, RemainingTimeMs = 0, CloseTheWindow = false, ExecutionTimeMs = value.ExecutionTimeMs, NameOfSkill = value.NameOfSkill, Status = "(EMERGENCY) STOP CALLED => EXECUTION FINISHED" }; OnDisplayOnSkillWindow(argsClose); OutputParams outp = new OutputParams(ERROR_DURING_EXECUTION, null, false, new SkillProException("(EMERGENCY) STOP CALLED")); return(outp); } catch (Exception ex) { return(new OutputParams(ERROR_DURING_EXECUTION, null, false, new SkillProException(ex.Message, ex))); } }
private static OutputParams InterativeAskReturnedCode(InputsForFakeWindow value, int errorCode) { //Here ask the user the returned code. // YES : do as if the execution worked well //NO : simulate that there were an error in the process System.Windows.MessageBoxResult res = System.Windows.MessageBox.Show("Press \"YES\" to return error code == 0 (no error during execution)" + Environment.NewLine + " or " + Environment.NewLine + "press \"NO\" to return error code == " + errorCode + " (simulate that an error occured during execution)", "Question", System.Windows.MessageBoxButton.YesNo); OutputParams outp; switch (res) { case System.Windows.MessageBoxResult.OK: outp = new OutputParams(0, value); break; case System.Windows.MessageBoxResult.Yes: outp = new OutputParams(0, value); break; default: throw new Exception("User decided to return the error code " + errorCode + " by clicking on the \"NO\" button"); } return(outp); }
private OutputParams ProcessWindow(InputParams inputs, ref bool SkillIsPausable) { //This is the implementation of the "first" Method provided to the Skills (the "process method") //Remark : this method is quite complicated because it has to deal with windows and HMI, so it requires access to the UI thread. //Skill is currently not pausable SkillIsPausable = false; InputsForFakeWindow value; //First cast the inputs try { value = (InputsForFakeWindow)inputs.Values; } catch (System.InvalidCastException ex) { //If you can't cast the inputs, then there is a special error code for this return(new OutputParams(Skill.ERROR_VALUES_PROCESS_INVALID, null)); } //Then execute the code try { //Lets say skill is productive (even if it is not that true) inputs.IsProductive = true; //Update remaining time value.RemainingTimeMs = value.ExecutionTimeMs; inputs.RemainingDuration = (UInt32)value.RemainingTimeMs; //First Ask (the UI thread) to pop a new window InputsForFakeWindow argsFirst = new InputsForFakeWindow() { StartANewWindow = true, RemainingTimeMs = value.RemainingTimeMs, CloseTheWindow = false, ExecutionTimeMs = value.ExecutionTimeMs, NameOfSkill = value.NameOfSkill, Status = "EXECUTING" }; OnDisplayOnSkillWindow(argsFirst); //Then perform a loop, each second, after each second int subdivision = value.ExecutionTimeMs / 1000; //You can subscribe to the "Pausing" event, if you want to have notification of the Pausing. //The SEE mother class also subscribed to that event, to notify the OPC-UA about the state change inputs.Pausing += inputs_Pausing; if (subdivision >= 1) { //SKill is now pausable => turn SkillIsPausable to true SkillIsPausable = true; //there are more than 1 second of execution time for (int i = 0; i < subdivision; i++) { double remainingTime = (((double)value.ExecutionTimeMs) - i * 1000.0); inputs.StopOrPauseIfRequired(); //You can use this line of code to block or stop the execution, if a Stop or pause was required InputsForFakeWindow argsLoop = new InputsForFakeWindow() { StartANewWindow = false, RemainingTimeMs = (int)remainingTime, CloseTheWindow = false, ExecutionTimeMs = value.ExecutionTimeMs, NameOfSkill = value.NameOfSkill, Status = "EXECUTING" }; OnDisplayOnSkillWindow(argsLoop); //Update remaining time before sleeping inputs.RemainingDuration = (UInt32)remainingTime; //Sleep 1s System.Threading.Thread.Sleep(1000); inputs.StopOrPauseIfRequired(); //You can use this line of code to block or stop the execution, if a Stop or pause was required } double LastValue = (((double)value.ExecutionTimeMs) - subdivision * 1000.0); inputs.StopOrPauseIfRequired(); //You can use this line of code to block or stop the execution, if a Stop or pause was required InputsForFakeWindow argsLastLoop = new InputsForFakeWindow() { StartANewWindow = false, RemainingTimeMs = (int)LastValue, CloseTheWindow = false, ExecutionTimeMs = value.ExecutionTimeMs, NameOfSkill = value.NameOfSkill, Status = "EXECUTING" }; OnDisplayOnSkillWindow(argsLastLoop); //SKill is no longer pausable => turn SkillIsPausable to false SkillIsPausable = false; inputs.StopOrPauseIfRequired(); inputs.Pausing -= inputs_Pausing; //Update remaining time before sleeping inputs.RemainingDuration = (UInt32)LastValue; //Sleep the remaining time System.Threading.Thread.Sleep((int)LastValue); } else { //there are Less than 1 second of execution time //SKill is now pausable => turn SkillIsPausable to true SkillIsPausable = true; inputs.StopOrPauseIfRequired();//You can use this line of code to block or stop the execution, if a Stop or pause was required InputsForFakeWindow argsLess1 = new InputsForFakeWindow() { StartANewWindow = false, RemainingTimeMs = value.ExecutionTimeMs, CloseTheWindow = false, ExecutionTimeMs = value.ExecutionTimeMs, NameOfSkill = value.NameOfSkill, Status = "EXECUTING" }; OnDisplayOnSkillWindow(argsLess1); //SKill is no longer pausable => turn SkillIsPausable to false SkillIsPausable = false; inputs.StopOrPauseIfRequired(); inputs.Pausing -= inputs_Pausing; //Update remaining time before sleeping inputs.RemainingDuration = (UInt32)value.ExecutionTimeMs; //Sleep System.Threading.Thread.Sleep(value.ExecutionTimeMs); } //Display that skill execution is about to finish InputsForFakeWindow argsAboutToClose = new InputsForFakeWindow() { StartANewWindow = false, RemainingTimeMs = 0, CloseTheWindow = false, ExecutionTimeMs = value.ExecutionTimeMs, NameOfSkill = value.NameOfSkill, Status = "ABOUT TO FINISH : ANSWER QUESTION FIRST" }; OnDisplayOnSkillWindow(argsAboutToClose); //Then finnaly, you have to prepare the "OutputParams". //These "OutputParams" then returned in the overriden method GetOutputsOfExecutedSkill //So take care of what you are providing OutputParams outp = InterativeAskReturnedCode(value, ERROR_DURING_EXECUTION); InputsForFakeWindow argsClose = new InputsForFakeWindow() { StartANewWindow = false, RemainingTimeMs = 0, CloseTheWindow = false, ExecutionTimeMs = value.ExecutionTimeMs, NameOfSkill = value.NameOfSkill, Status = "FINISHED" }; OnDisplayOnSkillWindow(argsClose); //Check if you have an alternative post condition if (value.AlternativePostConditionAvailable && outp.ReturnCode == 0) { //Ask user if he wants to go to alternative post condition System.Windows.MessageBoxResult res = System.Windows.MessageBox.Show("Do you want to go to alternative post condition?", "Alternative", System.Windows.MessageBoxButton.YesNo); switch (res) { case System.Windows.MessageBoxResult.Yes: outp.GoToAlternativePostCondition = true; break; default: outp.GoToAlternativePostCondition = false; break; } } //you don't have to Update remaining time to "0" => it is already done in the SEE mother class return(outp); } catch (OperationCanceledException ex) { //External element asked for "emergency stop". //The "Closing Method" will be called (second method passed to the constructor of the skill) //You can do some memory freeing here. //And then, rethrow the exception throw; } catch (Exception ex) { return(new OutputParams(ERROR_DURING_EXECUTION, null, false, new SkillProException(ex.Message, ex))); } }