/// <summary>
        /// Sets the parameters that are common to all EvaluationJobs. This should be called by the
        /// class inheriting from this class during configuration.
        /// </summary>
        protected void SetExpressionBaseParameters(EvaluationInput input)
        {
            if (this.expression != null)
            {
                char charVarName = 'a';
                foreach (MssParamInfo varInfo in input.VariableParamInfoList)
                {
                    double relVal = CustomMathUtils.AbsToRelVal(varInfo.MinValue, varInfo.MaxValue, varInfo.GetValue());

                    this.expression.Parameters[varInfo.Name.ToLower()]          = varInfo.GetValue();
                    this.expression.Parameters[varInfo.Name.ToLower() + "_rel"] = relVal;

                    if (charVarName <= 'z')
                    {
                        this.expression.Parameters[charVarName.ToString()]          = varInfo.GetValue();
                        this.expression.Parameters[charVarName.ToString() + "_rel"] = relVal;
                        charVarName++;
                    }
                }

                int paramNum = 1;
                foreach (MssParamInfo varInfo in input.TransformParamInfoList)
                {
                    double relVal = CustomMathUtils.AbsToRelVal(varInfo.MinValue, varInfo.MaxValue, varInfo.GetValue());

                    this.expression.Parameters[varInfo.Name.ToLower()]          = varInfo.GetValue();
                    this.expression.Parameters[varInfo.Name.ToLower() + "_rel"] = relVal;

                    this.expression.Parameters["p" + paramNum.ToString()]          = varInfo.GetValue();
                    this.expression.Parameters["p" + paramNum.ToString() + "_rel"] = relVal;
                    paramNum++;
                }
            }
            else
            {
                //This function should not be called when the expression is null.
                Debug.Assert(false);
            }
        }
        protected void ParameterValueChanged(MssParameterID paramId, double newValue)
        {
            lock (MssComponentHub.criticalSectioinLock)
            {
                if (this.hostInfoOutput.SampleRateIsInitialized == false)
                {
                    return;
                }

                long watchOffestAtParamChanged = this.stopwatch.Elapsed.Ticks;

                MssParamInfo paramInfo = this.mssParameters.GetParameterInfoCopy(paramId);
                double       relValue  = CustomMathUtils.AbsToRelVal(paramInfo.MinValue, paramInfo.MaxValue, newValue);

                MssMsg paramMsg = new MssMsg(MssMsgType.Parameter,
                                             (int)paramId,
                                             MssMsgUtil.UNUSED_MSS_MSG_DATA,
                                             relValue);
                MssEvent paramEvent = new MssEvent();

                paramEvent.mssMsg = paramMsg;

                long   ticksSinceCycleEnd   = watchOffestAtParamChanged - this.watchOffestAtLastCycleEnd;
                double secondsSinceCycleEnd = ticksSinceCycleEnd / (double)TimeSpan.TicksPerSecond;
                long   samplesSinceCycleEnd = (long)Math.Round(secondsSinceCycleEnd * this.hostInfoOutput.SampleRate);

                //Sometimes the stopwatch may not be accurate enough to notice the difference
                //between the end of the last cycle and now. Setting samplesSinceCycleEnd to
                //1 will cause this event to happen at the very start of this processing cycle.
                if (samplesSinceCycleEnd == 0)
                {
                    samplesSinceCycleEnd = 1;
                }

                paramEvent.sampleTime = this.sampleTimeAtLastCycleEnd + samplesSinceCycleEnd;

                this.dryEventInput.ReceiveDryMssEvent(paramEvent);
            }
        }