Files
ThingsGateway/src/Admin/ThingsGateway.Furion/Schedule/Triggers/Trigger.Methods.cs
2025-10-15 17:40:33 +08:00

667 lines
27 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// ------------------------------------------------------------------------
// 版权信息
// 版权归百小僧及百签科技(广东)有限公司所有。
// 所有权利保留。
// 官方网站https://baiqian.com
//
// 许可证信息
// 项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。
// 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。
// ------------------------------------------------------------------------
using System.Collections.Concurrent;
using ThingsGateway.Templates;
namespace ThingsGateway.Schedule;
/// <summary>
/// 作业触发器基类
/// </summary>
public partial class Trigger
{
/// <summary>
/// 计算下一个触发时间
/// </summary>
/// <param name="startAt">起始时间</param>
/// <returns><see cref="DateTime"/></returns>
public virtual DateTime GetNextOccurrence(DateTime startAt) => throw new NotImplementedException();
/// <summary>
/// 执行条件检查
/// </summary>
/// <param name="jobDetail">作业信息</param>
/// <param name="startAt">起始时间</param>
/// <returns><see cref="bool"/></returns>
public virtual bool ShouldRun(JobDetail jobDetail, DateTime startAt)
{
return NextRunTime.Value <= startAt
&& LastRunTime != NextRunTime;
}
/// <summary>
/// 获取作业触发器构建器
/// </summary>
/// <returns><see cref="TriggerBuilder"/></returns>
public TriggerBuilder GetBuilder()
{
return TriggerBuilder.From(this);
}
/// <summary>
/// 获取作业触发器最近运行时间
/// </summary>
/// <returns></returns>
public IEnumerable<TriggerTimeline> GetTimelines()
{
return Timelines.OrderByDescending(u => u.CreatedTime).ToList();
}
/// <summary>
/// 作业触发器转字符串输出
/// </summary>
/// <returns><see cref="string"/></returns>
public override string ToString()
{
return $"<{JobId} {TriggerId}>{(string.IsNullOrWhiteSpace(Description) ? string.Empty : $" {Description.GetMaxLengthString()}")} {NumberOfRuns}ts";
}
/// <summary>
/// 重置启动时最大触发次数等于一次的作业触发器
/// </summary>
/// <param name="startAt">起始时间</param>
internal void ResetMaxNumberOfRunsEqualOnceOnStart(DateTime startAt)
{
if (StartNow
&& ResetOnlyOnce
&& MaxNumberOfRuns == 1
&& NumberOfRuns == 1)
{
NumberOfRuns = 0;
SetStatus(TriggerStatus.Ready);
NextRunTime = CheckRunOnStartAndReturnNextRunTime(startAt);
if (MaxNumberOfErrors > 0 && NumberOfErrors >= MaxNumberOfErrors)
{
NumberOfErrors = MaxNumberOfErrors - 1;
}
}
}
/// <summary>
/// 记录运行信息和计算下一个触发时间
/// </summary>
/// <param name="jobDetail">作业信息</param>
/// <param name="startAt">起始时间</param>
internal void Increment(JobDetail jobDetail, DateTime startAt)
{
// 阻塞状态并没有实际执行,此时忽略次数递增和最近运行时间赋值
if (Status != TriggerStatus.Blocked)
{
NumberOfRuns++;
// 处理立即执行的情况
LastRunTime = NextRunTime == null
? startAt
: (startAt < NextRunTime.Value
? startAt
: NextRunTime);
}
NextRunTime = GetNextRunTime(startAt);
// 检查下一次执行信息
CheckAndFixNextOccurrence(jobDetail, startAt);
}
/// <summary>
/// 记录错误信息,包含错误次数和运行状态
/// </summary>
/// <param name="jobDetail">作业信息</param>
/// <param name="startAt">起始时间</param>
internal void IncrementErrors(JobDetail jobDetail, DateTime startAt)
{
NumberOfErrors++;
// 检查下一次执行信息
if (CheckAndFixNextOccurrence(jobDetail, startAt)) SetStatus(TriggerStatus.ErrorToReady);
}
/// <summary>
/// 计算下一次运行时间
/// </summary>
/// <param name="startAt">起始时间</param>
/// <returns><see cref="DateTime"/></returns>
internal DateTime? GetNextRunTime(DateTime startAt)
{
// 如果未启动或不是正常的触发器状态,则返回 null
if (StartNow == false || (Status != TriggerStatus.Backlog
&& Status != TriggerStatus.Ready
&& Status != TriggerStatus.ErrorToReady
&& Status != TriggerStatus.Running
&& Status != TriggerStatus.Blocked)) return null;
// 如果已经设置了 NextRunTime 且其值大于当前时间,则返回当前 NextRunTime可能因为其他方式修改了该值导致触发时间不是精准计算的时间
if (NextRunTime != null && NextRunTime.Value > startAt) return NextRunTime;
var baseTime = GetStartAt(startAt);
if (baseTime == null) return null;
// 获取下一次执行时间
return GetNextOccurrence(baseTime.Value);
}
/// <summary>
/// 检查是否启动时执行一次并返回下一次执行时间
/// </summary>
/// <param name="startAt">起始时间</param>
/// <returns><see cref="DateTime"/></returns>
internal DateTime? CheckRunOnStartAndReturnNextRunTime(DateTime startAt)
{
// 检查是否启动时需要执行
if (!(StartNow && RunOnStart))
{
return GetNextRunTime(startAt);
}
else
{
// 检查是否设置了 RunOnStartProvider 配置
if (ScheduleOptionsBuilder.InternalRunOnStartProvider is null)
{
return (StartTime == null || StartTime.Value <= startAt)
? startAt.AddSeconds(-1)
: StartTime;
}
return ScheduleOptionsBuilder.InternalRunOnStartProvider(this, startAt);
}
}
/// <summary>
/// 设置作业触发器状态
/// </summary>
/// <param name="status"><see cref="TriggerStatus"/></param>
internal void SetStatus(TriggerStatus status)
{
if (Status == status) return;
Status = status;
}
/// <summary>
/// 是否是正常触发器状态
/// </summary>
/// <returns><see cref="bool"/></returns>
internal bool IsNormalStatus()
{
var isNormalStatus = Status != TriggerStatus.Pause
&& Status != TriggerStatus.Archived
&& Status != TriggerStatus.Panic
&& Status != TriggerStatus.Overrun
&& Status != TriggerStatus.Unoccupied
&& Status != TriggerStatus.NotStart
&& Status != TriggerStatus.Unknown
&& Status != TriggerStatus.Unhandled;
// 如果不是正常触发器状态NextRunTime 强制设置为 null
if (!isNormalStatus) NextRunTime = null;
return isNormalStatus;
}
/// <summary>
/// 检查下一次执行信息并修正 <see cref="NextRunTime"/> 和 <see cref="Status"/>
/// </summary>
/// <param name="jobDetail">作业信息</param>
/// <param name="startAt">起始时间</param>
/// <returns><see cref="bool"/></returns>
internal bool CheckAndFixNextOccurrence(JobDetail jobDetail, DateTime startAt)
{
// 检查作业信息运行时类型
if (jobDetail.RuntimeJobType == null)
{
SetStatus(TriggerStatus.Unhandled);
NextRunTime = null;
return false;
}
// 检查作业触发器运行时类型
if (RuntimeTriggerType == null)
{
SetStatus(TriggerStatus.Unknown);
NextRunTime = null;
return false;
}
// 检查是否立即启动
if (StartNow == false)
{
SetStatus(TriggerStatus.NotStart);
NextRunTime = null;
return false;
}
// 开始时间检查
if (StartTime != null)
{
if (StartTime.Value > startAt)
{
SetStatus(TriggerStatus.Backlog);
return true;
}
}
// 结束时间检查
if (EndTime != null)
{
var compareTime = NextRunTime != null ? NextRunTime.Value : startAt;
if (EndTime.Value < compareTime)
{
SetStatus(TriggerStatus.Archived);
NextRunTime = null;
return false;
}
}
// 最大次数判断
if (MaxNumberOfRuns > 0 && NumberOfRuns >= MaxNumberOfRuns)
{
SetStatus(TriggerStatus.Overrun);
NextRunTime = null;
return false;
}
// 最大错误数判断
if (MaxNumberOfErrors > 0 && NumberOfErrors >= MaxNumberOfErrors)
{
SetStatus(TriggerStatus.Panic);
NextRunTime = null;
return false;
}
// 状态检查
if (!IsNormalStatus())
{
return false;
}
// 下一次运行时间空判断
if (NextRunTime == null)
{
if (IsNormalStatus()) SetStatus(TriggerStatus.Unoccupied);
return false;
}
return true;
}
/// <summary>
/// 下一次可执行检查
/// </summary>
/// <param name="startAt">起始时间</param>
/// <returns><see cref="bool"/></returns>
internal bool NextShouldRun(DateTime startAt)
{
return IsNormalStatus()
&& NextRunTime != null
&& NextRunTime.Value >= startAt;
}
/// <summary>
/// 当前可执行检查
/// </summary>
/// <param name="jobDetail">作业信息</param>
/// <param name="startAt">起始时间</param>
/// <returns><see cref="bool"/></returns>
internal bool CurrentShouldRun(JobDetail jobDetail, DateTime startAt)
{
return CheckAndFixNextOccurrence(jobDetail, startAt)
// 调用派生类 ShouldRun 方法
&& ShouldRun(jobDetail, startAt);
}
/// <summary>
/// 记录作业触发器运行信息
/// </summary>
/// <param name="schedulerFactory">作业计划工厂</param>
/// <param name="jobId">作业 Id</param>
/// <param name="exception">异常信息</param>
/// <returns><see cref="Task"/></returns>
internal async Task RecordTimelineAsync(ISchedulerFactory schedulerFactory, string jobId, string exception = null)
{
Timelines ??= new();
// 只保留 5 条记录
if (Timelines.Count >= 5)
{
var _timelines = Timelines.Dequeue();
_timelines?.Dispose();
}
var timeline = new TriggerTimeline
{
JobId = jobId,
TriggerId = TriggerId,
LastRunTime = LastRunTime,
NumberOfRuns = NumberOfRuns,
NextRunTime = NextRunTime,
Status = Status,
Result = Result,
ElapsedTime = ElapsedTime,
CreatedTime = DateTime.Now,
Mode = Mode,
Exception = exception
};
Timelines.Enqueue(timeline);
// 调用事件委托(记录作业触发器运行信息)
if (schedulerFactory is SchedulerFactory schedulerFactoryInstance)
{
// 获取作业计划
var scheduler = schedulerFactoryInstance.GetJob(jobId);
if (scheduler is null)
{
Timelines.Clear();
Timelines = null;
return;
}
// 初始化作业执行记录持久上下文
var context = new PersistenceExecutionRecordContext(
scheduler.GetJobDetail()
, this
, timeline.Mode
, timeline);
await schedulerFactoryInstance.RecordTimelineAsync(context).ConfigureAwait(false);
}
}
/// <summary>
/// 带命名规则的数据库列名
/// </summary>
private readonly NonBlockingDictionary<NamingConventions, string[]> _namingColumnNames = new();
/// <summary>
/// 获取数据库列名
/// </summary>
/// <param name="naming">命名法</param>
/// <returns>string[]</returns>
private string[] ColumnNames(NamingConventions naming = NamingConventions.CamelCase)
{
// 如果字典中已经存在过,则直接返回
var contains = _namingColumnNames.TryGetValue(naming, out var columnNames);
if (contains) return columnNames;
// 否则创建新的
var nameColumnNames = new[]
{
Penetrates.GetNaming(nameof(TriggerId), naming) // 第一个是标识,禁止移动位置
, Penetrates.GetNaming(nameof(JobId), naming) // 第二个是作业标识,禁止移动位置
, Penetrates.GetNaming(nameof(TriggerType), naming)
, Penetrates.GetNaming(nameof(AssemblyName), naming)
, Penetrates.GetNaming(nameof(Args), naming)
, Penetrates.GetNaming(nameof(Description), naming)
, Penetrates.GetNaming(nameof(Status), naming)
, Penetrates.GetNaming(nameof(StartTime), naming)
, Penetrates.GetNaming(nameof(EndTime), naming)
, Penetrates.GetNaming(nameof(LastRunTime), naming)
, Penetrates.GetNaming(nameof(NextRunTime), naming)
, Penetrates.GetNaming(nameof(NumberOfRuns), naming)
, Penetrates.GetNaming(nameof(MaxNumberOfRuns), naming)
, Penetrates.GetNaming(nameof(NumberOfErrors), naming)
, Penetrates.GetNaming(nameof(MaxNumberOfErrors), naming)
, Penetrates.GetNaming(nameof(NumRetries), naming)
, Penetrates.GetNaming(nameof(RetryTimeout), naming)
, Penetrates.GetNaming(nameof(StartNow), naming)
, Penetrates.GetNaming(nameof(RunOnStart), naming)
, Penetrates.GetNaming(nameof(ResetOnlyOnce), naming)
, Penetrates.GetNaming(nameof(Result), naming)
, Penetrates.GetNaming(nameof(ElapsedTime), naming)
, Penetrates.GetNaming(nameof(UpdatedTime), naming)
};
_ = _namingColumnNames.TryAdd(naming, nameColumnNames);
return nameColumnNames;
}
/// <summary>
/// 获取触发器初始化时间
/// </summary>
/// <param name="startAt">起始时间</param>
/// <returns><see cref="DateTime"/> 或者 null</returns>
private DateTime? GetStartAt(DateTime startAt)
{
if (StartTime == null)
{
if (EndTime == null) return startAt;
else return EndTime.Value < startAt ? null : startAt;
}
else return StartTime.Value < startAt ? startAt : StartTime.Value;
}
/// <summary>
/// 转换成 Sql 语句
/// </summary>
/// <param name="tableName">数据库表名</param>
/// <param name="behavior">持久化行为</param>
/// <param name="naming">命名法</param>
/// <returns><see cref="string"/></returns>
public string ConvertToSQL(string tableName, PersistenceBehavior behavior, NamingConventions naming = NamingConventions.CamelCase)
{
// 判断是否自定义了 SQL 输出程序
if (TriggerOptions.ConvertToSQLConfigure != null)
{
return TriggerOptions.ConvertToSQLConfigure(tableName, ColumnNames(naming), this, behavior, naming);
}
// 生成新增 SQL
if (behavior == PersistenceBehavior.Appended)
{
return ConvertToInsertSQL(tableName, naming);
}
// 生成更新 SQL
else if (behavior == PersistenceBehavior.Updated)
{
return ConvertToUpdateSQL(tableName, naming);
}
// 生成删除 SQL
else if (behavior == PersistenceBehavior.Removed)
{
return ConvertToDeleteSQL(tableName, naming);
}
return string.Empty;
}
/// <summary>
/// 转换成 Sql 新增语句
/// </summary>
/// <param name="tableName">数据库表名</param>
/// <param name="naming">命名法</param>
/// <returns><see cref="string"/></returns>
public string ConvertToInsertSQL(string tableName, NamingConventions naming = NamingConventions.CamelCase)
{
// 不使用反射生成,为了使顺序可控,生成 SQL 可控,性能损耗最小
var columnNames = ColumnNames(naming);
return $@"INSERT INTO {Penetrates.WrapDatabaseFieldName(tableName)}(
{Penetrates.WrapDatabaseFieldName(columnNames[0])},
{Penetrates.WrapDatabaseFieldName(columnNames[1])},
{Penetrates.WrapDatabaseFieldName(columnNames[2])},
{Penetrates.WrapDatabaseFieldName(columnNames[3])},
{Penetrates.WrapDatabaseFieldName(columnNames[4])},
{Penetrates.WrapDatabaseFieldName(columnNames[5])},
{Penetrates.WrapDatabaseFieldName(columnNames[6])},
{Penetrates.WrapDatabaseFieldName(columnNames[7])},
{Penetrates.WrapDatabaseFieldName(columnNames[8])},
{Penetrates.WrapDatabaseFieldName(columnNames[9])},
{Penetrates.WrapDatabaseFieldName(columnNames[10])},
{Penetrates.WrapDatabaseFieldName(columnNames[11])},
{Penetrates.WrapDatabaseFieldName(columnNames[12])},
{Penetrates.WrapDatabaseFieldName(columnNames[13])},
{Penetrates.WrapDatabaseFieldName(columnNames[14])},
{Penetrates.WrapDatabaseFieldName(columnNames[15])},
{Penetrates.WrapDatabaseFieldName(columnNames[16])},
{Penetrates.WrapDatabaseFieldName(columnNames[17])},
{Penetrates.WrapDatabaseFieldName(columnNames[18])},
{Penetrates.WrapDatabaseFieldName(columnNames[19])},
{Penetrates.WrapDatabaseFieldName(columnNames[20])},
{Penetrates.WrapDatabaseFieldName(columnNames[21])},
{Penetrates.WrapDatabaseFieldName(columnNames[22])}
)
VALUES(
{Penetrates.GetNoNumberSqlValueOrNull(TriggerId)},
{Penetrates.GetNoNumberSqlValueOrNull(JobId)},
{Penetrates.GetNoNumberSqlValueOrNull(TriggerType)},
{Penetrates.GetNoNumberSqlValueOrNull(AssemblyName)},
{Penetrates.GetNoNumberSqlValueOrNull(Args)},
{Penetrates.GetNoNumberSqlValueOrNull(Description)},
{((int)Status)},
{Penetrates.GetNoNumberSqlValueOrNull(StartTime.ToFormatString())},
{Penetrates.GetNoNumberSqlValueOrNull(EndTime.ToFormatString())},
{Penetrates.GetNoNumberSqlValueOrNull(LastRunTime.ToFormatString())},
{Penetrates.GetNoNumberSqlValueOrNull(NextRunTime.ToFormatString())},
{NumberOfRuns},
{MaxNumberOfRuns},
{NumberOfErrors},
{MaxNumberOfErrors},
{NumRetries},
{RetryTimeout},
{Penetrates.GetBooleanSqlValue(StartNow)},
{Penetrates.GetBooleanSqlValue(RunOnStart)},
{Penetrates.GetBooleanSqlValue(ResetOnlyOnce)},
{Penetrates.GetNoNumberSqlValueOrNull(Result)},
{ElapsedTime},
{Penetrates.GetNoNumberSqlValueOrNull(UpdatedTime.ToFormatString())}
);";
}
/// <summary>
/// 转换成 Sql 更新语句
/// </summary>
/// <param name="tableName">数据库表名</param>
/// <param name="naming">命名法</param>
/// <returns><see cref="string"/></returns>
public string ConvertToUpdateSQL(string tableName, NamingConventions naming = NamingConventions.CamelCase)
{
// 不使用反射生成,为了使顺序可控,生成 SQL 可控,性能损耗最小
var columnNames = ColumnNames(naming);
return $@"UPDATE {Penetrates.WrapDatabaseFieldName(tableName)}
SET
{Penetrates.WrapDatabaseFieldName(columnNames[0])} = {Penetrates.GetNoNumberSqlValueOrNull(TriggerId)},
{Penetrates.WrapDatabaseFieldName(columnNames[1])} = {Penetrates.GetNoNumberSqlValueOrNull(JobId)},
{Penetrates.WrapDatabaseFieldName(columnNames[2])} = {Penetrates.GetNoNumberSqlValueOrNull(TriggerType)},
{Penetrates.WrapDatabaseFieldName(columnNames[3])} = {Penetrates.GetNoNumberSqlValueOrNull(AssemblyName)},
{Penetrates.WrapDatabaseFieldName(columnNames[4])} = {Penetrates.GetNoNumberSqlValueOrNull(Args)},
{Penetrates.WrapDatabaseFieldName(columnNames[5])} = {Penetrates.GetNoNumberSqlValueOrNull(Description)},
{Penetrates.WrapDatabaseFieldName(columnNames[6])} = {((int)Status)},
{Penetrates.WrapDatabaseFieldName(columnNames[7])} = {Penetrates.GetNoNumberSqlValueOrNull(StartTime.ToFormatString())},
{Penetrates.WrapDatabaseFieldName(columnNames[8])} = {Penetrates.GetNoNumberSqlValueOrNull(EndTime.ToFormatString())},
{Penetrates.WrapDatabaseFieldName(columnNames[9])} = {Penetrates.GetNoNumberSqlValueOrNull(LastRunTime.ToFormatString())},
{Penetrates.WrapDatabaseFieldName(columnNames[10])} = {Penetrates.GetNoNumberSqlValueOrNull(NextRunTime.ToFormatString())},
{Penetrates.WrapDatabaseFieldName(columnNames[11])} = {NumberOfRuns},
{Penetrates.WrapDatabaseFieldName(columnNames[12])} = {MaxNumberOfRuns},
{Penetrates.WrapDatabaseFieldName(columnNames[13])} = {NumberOfErrors},
{Penetrates.WrapDatabaseFieldName(columnNames[14])} = {MaxNumberOfErrors},
{Penetrates.WrapDatabaseFieldName(columnNames[15])} = {NumRetries},
{Penetrates.WrapDatabaseFieldName(columnNames[16])} = {RetryTimeout},
{Penetrates.WrapDatabaseFieldName(columnNames[17])} = {Penetrates.GetBooleanSqlValue(StartNow)},
{Penetrates.WrapDatabaseFieldName(columnNames[18])} = {Penetrates.GetBooleanSqlValue(RunOnStart)},
{Penetrates.WrapDatabaseFieldName(columnNames[19])} = {Penetrates.GetBooleanSqlValue(ResetOnlyOnce)},
{Penetrates.WrapDatabaseFieldName(columnNames[20])} = {Penetrates.GetNoNumberSqlValueOrNull(Result)},
{Penetrates.WrapDatabaseFieldName(columnNames[21])} = {ElapsedTime},
{Penetrates.WrapDatabaseFieldName(columnNames[22])} = {Penetrates.GetNoNumberSqlValueOrNull(UpdatedTime.ToFormatString())}
WHERE {Penetrates.WrapDatabaseFieldName(columnNames[0])} = {Penetrates.GetNoNumberSqlValueOrNull(TriggerId)} AND {Penetrates.WrapDatabaseFieldName(columnNames[1])} = {Penetrates.GetNoNumberSqlValueOrNull(JobId)};";
}
/// <summary>
/// 转换成 Sql 删除语句
/// </summary>
/// <param name="tableName">数据库表名</param>
/// <param name="naming">命名法</param>
/// <returns><see cref="string"/></returns>
public string ConvertToDeleteSQL(string tableName, NamingConventions naming = NamingConventions.CamelCase)
{
// 不使用反射生成,为了使顺序可控,生成 SQL 可控,性能损耗最小
var columnNames = ColumnNames(naming);
return $@"DELETE FROM {Penetrates.WrapDatabaseFieldName(tableName)}
WHERE {Penetrates.WrapDatabaseFieldName(columnNames[0])} = {Penetrates.GetNoNumberSqlValueOrNull(TriggerId)} AND {Penetrates.WrapDatabaseFieldName(columnNames[1])} = {Penetrates.GetNoNumberSqlValueOrNull(JobId)};";
}
/// <summary>
/// 转换成 JSON 字符串
/// </summary>
/// <param name="naming">命名法</param>
/// <returns><see cref="string"/></returns>
public string ConvertToJSON(NamingConventions naming = NamingConventions.CamelCase)
{
return Penetrates.Write(writer =>
{
writer.WriteStartObject();
writer.WriteString(Penetrates.GetNaming(nameof(TriggerId), naming), TriggerId);
writer.WriteString(Penetrates.GetNaming(nameof(JobId), naming), JobId);
writer.WriteString(Penetrates.GetNaming(nameof(TriggerType), naming), TriggerType);
writer.WriteString(Penetrates.GetNaming(nameof(AssemblyName), naming), AssemblyName);
writer.WriteString(Penetrates.GetNaming(nameof(Args), naming), Args);
writer.WriteString(Penetrates.GetNaming(nameof(Description), naming), Description);
writer.WriteNumber(Penetrates.GetNaming(nameof(Status), naming), (int)Status);
writer.WriteString(Penetrates.GetNaming(nameof(StartTime), naming), StartTime.ToFormatString());
writer.WriteString(Penetrates.GetNaming(nameof(EndTime), naming), EndTime.ToFormatString());
writer.WriteString(Penetrates.GetNaming(nameof(LastRunTime), naming), LastRunTime.ToFormatString());
writer.WriteString(Penetrates.GetNaming(nameof(NextRunTime), naming), NextRunTime.ToFormatString());
writer.WriteNumber(Penetrates.GetNaming(nameof(NumberOfRuns), naming), NumberOfRuns);
writer.WriteNumber(Penetrates.GetNaming(nameof(MaxNumberOfRuns), naming), MaxNumberOfRuns);
writer.WriteNumber(Penetrates.GetNaming(nameof(NumberOfErrors), naming), NumberOfErrors);
writer.WriteNumber(Penetrates.GetNaming(nameof(MaxNumberOfErrors), naming), MaxNumberOfErrors);
writer.WriteNumber(Penetrates.GetNaming(nameof(NumRetries), naming), NumRetries);
writer.WriteNumber(Penetrates.GetNaming(nameof(RetryTimeout), naming), RetryTimeout);
writer.WriteBoolean(Penetrates.GetNaming(nameof(StartNow), naming), StartNow);
writer.WriteBoolean(Penetrates.GetNaming(nameof(RunOnStart), naming), RunOnStart);
writer.WriteBoolean(Penetrates.GetNaming(nameof(ResetOnlyOnce), naming), ResetOnlyOnce);
writer.WriteString(Penetrates.GetNaming(nameof(Result), naming), Result);
writer.WriteNumber(Penetrates.GetNaming(nameof(ElapsedTime), naming), ElapsedTime);
writer.WriteString(Penetrates.GetNaming(nameof(UpdatedTime), naming), UpdatedTime.ToFormatString());
writer.WriteEndObject();
});
}
/// <summary>
/// 转换成 Monitor 字符串
/// </summary>
/// <param name="naming">命名法</param>
/// <returns><see cref="string"/></returns>
public string ConvertToMonitor(NamingConventions naming = NamingConventions.CamelCase)
{
return TP.Wrapper(nameof(Trigger), Description ?? TriggerType, new[]
{
$"##{Penetrates.GetNaming(nameof(TriggerId), naming)}## {TriggerId}"
, $"##{Penetrates.GetNaming(nameof(JobId), naming)}## {JobId}"
, $"##{Penetrates.GetNaming(nameof(TriggerType), naming)}## {TriggerType}"
, $"##{Penetrates.GetNaming(nameof(AssemblyName), naming)}## {AssemblyName}"
, $"##{Penetrates.GetNaming(nameof(Args), naming)}## {Args}"
, $"##{Penetrates.GetNaming(nameof(Description), naming)}## {Description}"
, $"##{Penetrates.GetNaming(nameof(Status), naming)}## {Status}"
, $"##{Penetrates.GetNaming(nameof(StartTime), naming)}## {StartTime.ToFormatString()}"
, $"##{Penetrates.GetNaming(nameof(EndTime), naming)}## {EndTime.ToFormatString()}"
, $"##{Penetrates.GetNaming(nameof(LastRunTime), naming)}## {LastRunTime.ToFormatString()}"
, $"##{Penetrates.GetNaming(nameof(NextRunTime), naming)}## {NextRunTime.ToFormatString()}"
, $"##{Penetrates.GetNaming(nameof(NumberOfRuns), naming)}## {NumberOfRuns}"
, $"##{Penetrates.GetNaming(nameof(MaxNumberOfRuns), naming)}## {MaxNumberOfRuns}"
, $"##{Penetrates.GetNaming(nameof(NumberOfErrors), naming)}## {NumberOfErrors}"
, $"##{Penetrates.GetNaming(nameof(MaxNumberOfErrors), naming)}## {MaxNumberOfErrors}"
, $"##{Penetrates.GetNaming(nameof(NumRetries), naming)}## {NumRetries}"
, $"##{Penetrates.GetNaming(nameof(RetryTimeout), naming)}## {RetryTimeout}"
, $"##{Penetrates.GetNaming(nameof(StartNow), naming)}## {StartNow}"
, $"##{Penetrates.GetNaming(nameof(RunOnStart), naming)}## {RunOnStart}"
, $"##{Penetrates.GetNaming(nameof(ResetOnlyOnce), naming)}## {ResetOnlyOnce}"
, $"##{Penetrates.GetNaming(nameof(Result), naming)}## {Result}"
, $"##{Penetrates.GetNaming(nameof(ElapsedTime), naming)}## {ElapsedTime}"
, $"##{Penetrates.GetNaming(nameof(UpdatedTime), naming)}## {UpdatedTime.ToFormatString()}"
});
}
}