Files
ThingsGateway/src/Admin/ThingsGateway.Furion/Schedule/Triggers/Trigger.Methods.cs

667 lines
27 KiB
C#
Raw Normal View History

2025-01-24 22:42:26 +08:00
// ------------------------------------------------------------------------
// 版权信息
// 版权归百小僧及百签科技(广东)有限公司所有。
// 所有权利保留。
// 官方网站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)
{
2025-09-04 22:39:21 +08:00
// 获取作业计划
var scheduler = schedulerFactoryInstance.GetJob(jobId);
if (scheduler is null)
{
Timelines.Clear();
Timelines = null;
return;
}
2025-01-24 22:42:26 +08:00
// 初始化作业执行记录持久上下文
var context = new PersistenceExecutionRecordContext(
2025-09-04 22:39:21 +08:00
scheduler.GetJobDetail()
2025-01-24 22:42:26 +08:00
, this
, timeline.Mode
, timeline);
await schedulerFactoryInstance.RecordTimelineAsync(context).ConfigureAwait(false);
}
}
/// <summary>
/// 带命名规则的数据库列名
/// </summary>
private readonly NonBlockingDictionary<NamingConventions, string[]> _namingColumnNames = new();
2025-01-24 22:42:26 +08:00
/// <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()}"
});
}
}