XP.Common 类库中新增授权管理(License Management)功能模块,支持两种授权模式:CLMS 正式授权和临时测试模式。开发统一的授权服务接口,并在主项目中完成集成。
This commit is contained in:
Binary file not shown.
@@ -1,43 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace XP.Common.Configs
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Serilog日志配置实体(从App.config读取)
|
|
||||||
/// </summary>
|
|
||||||
public class SerilogConfig
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 日志输出根路径(默认:AppData/Files/Logs)
|
|
||||||
/// </summary>
|
|
||||||
public string LogPath { get; set; } = Path.Combine(
|
|
||||||
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
|
||||||
"Files", "Logs");
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 最低日志级别(Debug/Info/Warn/Error/Fatal)
|
|
||||||
/// </summary>
|
|
||||||
public string MinimumLevel { get; set; } = "Info";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 是否输出到控制台(调试环境=true,生产环境=false)
|
|
||||||
/// </summary>
|
|
||||||
public bool EnableConsole { get; set; } = true;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 日志文件分割规则(Day/Month/Hour/Size)
|
|
||||||
/// </summary>
|
|
||||||
public string RollingInterval { get; set; } = "Day";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 单个日志文件最大大小(MB,仅Size分割时生效)
|
|
||||||
/// </summary>
|
|
||||||
public long FileSizeLimitMB { get; set; } = 100;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 保留日志文件数量(默认30天)
|
|
||||||
/// </summary>
|
|
||||||
public int RetainedFileCountLimit { get; set; } = 30;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace XP.Common.Configs
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// SQLite 配置实体
|
|
||||||
/// </summary>
|
|
||||||
public class SqliteConfig
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 数据库文件路径
|
|
||||||
/// </summary>
|
|
||||||
public string DbFilePath { get; set; } = Path.Combine(
|
|
||||||
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
|
||||||
"Files", "Data", "XP.db");
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 连接超时时间(秒,默认30)
|
|
||||||
/// </summary>
|
|
||||||
public int ConnectionTimeout { get; set; } = 30;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 数据库不存在时是否自动创建(默认true)
|
|
||||||
/// </summary>
|
|
||||||
public bool CreateIfNotExists { get; set; } = true;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 是否启用 WAL 模式(提升并发性能,默认true)
|
|
||||||
/// </summary>
|
|
||||||
public bool EnableWalMode { get; set; } = true;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 是否开启日志记录(记录所有SQL操作,默认false)
|
|
||||||
/// </summary>
|
|
||||||
public bool EnableSqlLogging { get; set; } = false;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取SQLite连接字符串
|
|
||||||
/// </summary>
|
|
||||||
public string GetConnectionString()
|
|
||||||
{
|
|
||||||
var builder = new Microsoft.Data.Sqlite.SqliteConnectionStringBuilder
|
|
||||||
{
|
|
||||||
DataSource = DbFilePath,
|
|
||||||
Cache = Microsoft.Data.Sqlite.SqliteCacheMode.Default,
|
|
||||||
DefaultTimeout = ConnectionTimeout
|
|
||||||
};
|
|
||||||
return builder.ToString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
using System.Configuration;
|
|
||||||
using XP.Common.Configs;
|
|
||||||
using XP.Common.Dump.Configs;
|
|
||||||
|
|
||||||
namespace XP.Common.Helpers
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 通用配置加载工具(读取App.config)
|
|
||||||
/// </summary>
|
|
||||||
public static class ConfigLoader
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 加载Serilog配置
|
|
||||||
/// </summary>
|
|
||||||
public static SerilogConfig LoadSerilogConfig()
|
|
||||||
{
|
|
||||||
var config = new SerilogConfig();
|
|
||||||
|
|
||||||
var logPath = ConfigurationManager.AppSettings["Serilog:LogPath"];
|
|
||||||
if (!string.IsNullOrEmpty(logPath)) config.LogPath = logPath;
|
|
||||||
|
|
||||||
var minLevel = ConfigurationManager.AppSettings["Serilog:MinimumLevel"];
|
|
||||||
if (!string.IsNullOrEmpty(minLevel)) config.MinimumLevel = minLevel;
|
|
||||||
|
|
||||||
var enableConsole = ConfigurationManager.AppSettings["Serilog:EnableConsole"];
|
|
||||||
if (bool.TryParse(enableConsole, out var console)) config.EnableConsole = console;
|
|
||||||
|
|
||||||
var rollingInterval = ConfigurationManager.AppSettings["Serilog:RollingInterval"];
|
|
||||||
if (!string.IsNullOrEmpty(rollingInterval)) config.RollingInterval = rollingInterval;
|
|
||||||
|
|
||||||
var fileSize = ConfigurationManager.AppSettings["Serilog:FileSizeLimitMB"];
|
|
||||||
if (long.TryParse(fileSize, out var size)) config.FileSizeLimitMB = size;
|
|
||||||
|
|
||||||
var retainCount = ConfigurationManager.AppSettings["Serilog:RetainedFileCountLimit"];
|
|
||||||
if (int.TryParse(retainCount, out var count)) config.RetainedFileCountLimit = count;
|
|
||||||
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 加载SQLite配置
|
|
||||||
/// </summary>
|
|
||||||
public static SqliteConfig LoadSqliteConfig()
|
|
||||||
{
|
|
||||||
var config = new SqliteConfig();
|
|
||||||
|
|
||||||
var dbPath = ConfigurationManager.AppSettings["Sqlite:DbFilePath"];
|
|
||||||
if (!string.IsNullOrEmpty(dbPath)) config.DbFilePath = dbPath;
|
|
||||||
|
|
||||||
var timeout = ConfigurationManager.AppSettings["Sqlite:ConnectionTimeout"];
|
|
||||||
if (int.TryParse(timeout, out var t)) config.ConnectionTimeout = t;
|
|
||||||
|
|
||||||
var createIfNotExists = ConfigurationManager.AppSettings["Sqlite:CreateIfNotExists"];
|
|
||||||
if (bool.TryParse(createIfNotExists, out var c)) config.CreateIfNotExists = c;
|
|
||||||
|
|
||||||
var enableWal = ConfigurationManager.AppSettings["Sqlite:EnableWalMode"];
|
|
||||||
if (bool.TryParse(enableWal, out var w)) config.EnableWalMode = w;
|
|
||||||
|
|
||||||
var enableSqlLog = ConfigurationManager.AppSettings["Sqlite:EnableSqlLogging"];
|
|
||||||
if (bool.TryParse(enableSqlLog, out var l)) config.EnableSqlLogging = l;
|
|
||||||
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 加载 Dump 配置 | Load Dump configuration
|
|
||||||
/// </summary>
|
|
||||||
public static DumpConfig LoadDumpConfig()
|
|
||||||
{
|
|
||||||
var config = new DumpConfig();
|
|
||||||
|
|
||||||
var storagePath = ConfigurationManager.AppSettings["Dump:StoragePath"];
|
|
||||||
if (!string.IsNullOrEmpty(storagePath)) config.StoragePath = storagePath;
|
|
||||||
|
|
||||||
var enableScheduled = ConfigurationManager.AppSettings["Dump:EnableScheduledDump"];
|
|
||||||
if (bool.TryParse(enableScheduled, out var enabled)) config.EnableScheduledDump = enabled;
|
|
||||||
|
|
||||||
var interval = ConfigurationManager.AppSettings["Dump:ScheduledIntervalMinutes"];
|
|
||||||
if (int.TryParse(interval, out var min)) config.ScheduledIntervalMinutes = min;
|
|
||||||
|
|
||||||
var sizeLimit = ConfigurationManager.AppSettings["Dump:MiniDumpSizeLimitMB"];
|
|
||||||
if (long.TryParse(sizeLimit, out var size)) config.MiniDumpSizeLimitMB = size;
|
|
||||||
|
|
||||||
var retentionDays = ConfigurationManager.AppSettings["Dump:RetentionDays"];
|
|
||||||
if (int.TryParse(retentionDays, out var days)) config.RetentionDays = days;
|
|
||||||
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
using System.Configuration;
|
||||||
|
|
||||||
|
namespace XP.Common.License.Configs
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 授权配置加载器,从 App.config 读取授权相关配置项 | License configuration loader, reads license-related configuration from App.config
|
||||||
|
/// </summary>
|
||||||
|
public static class ConfigLoader
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 配置键前缀 | Configuration key prefix
|
||||||
|
/// </summary>
|
||||||
|
private const string KeyPrefix = "License:";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// LicenseMode 有效值集合 | Valid values for LicenseMode
|
||||||
|
/// </summary>
|
||||||
|
private static readonly int[] ValidLicenseModes = { 0, 885 };
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// LicenseState 有效值集合 | Valid values for LicenseState
|
||||||
|
/// </summary>
|
||||||
|
private static readonly int[] ValidLicenseStates = { 10, 20 };
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 从 App.config 加载授权配置 | Load license configuration from App.config
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>授权配置实体,缺失或无效配置项使用默认值 | License configuration entity, uses default values for missing or invalid items</returns>
|
||||||
|
public static LicenseConfig LoadLicenseConfig()
|
||||||
|
{
|
||||||
|
var config = new LicenseConfig();
|
||||||
|
|
||||||
|
// 加载 LicenseMode | Load LicenseMode
|
||||||
|
var licenseModeStr = ConfigurationManager.AppSettings[KeyPrefix + "LicenseMode"];
|
||||||
|
if (int.TryParse(licenseModeStr, out var licenseMode) && IsValidLicenseMode(licenseMode))
|
||||||
|
{
|
||||||
|
config.LicenseMode = licenseMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载 ModuleId | Load ModuleId
|
||||||
|
var moduleIdStr = ConfigurationManager.AppSettings[KeyPrefix + "ModuleId"];
|
||||||
|
if (ushort.TryParse(moduleIdStr, out var moduleId) && IsValidModuleId(moduleId))
|
||||||
|
{
|
||||||
|
config.ModuleId = moduleId;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载 UseSma | Load UseSma
|
||||||
|
var useSmaStr = ConfigurationManager.AppSettings[KeyPrefix + "UseSma"];
|
||||||
|
if (bool.TryParse(useSmaStr, out var useSma))
|
||||||
|
{
|
||||||
|
config.UseSma = useSma;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载 LicenseState | Load LicenseState
|
||||||
|
var licenseStateStr = ConfigurationManager.AppSettings[KeyPrefix + "LicenseState"];
|
||||||
|
if (int.TryParse(licenseStateStr, out var licenseState) && IsValidLicenseState(licenseState))
|
||||||
|
{
|
||||||
|
config.LicenseState = licenseState;
|
||||||
|
}
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证 LicenseMode 值是否有效 | Validate whether LicenseMode value is valid
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">待验证的值 | Value to validate</param>
|
||||||
|
/// <returns>true 表示有效,false 表示无效 | true if valid, false if invalid</returns>
|
||||||
|
private static bool IsValidLicenseMode(int value)
|
||||||
|
{
|
||||||
|
foreach (var valid in ValidLicenseModes)
|
||||||
|
{
|
||||||
|
if (value == valid) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证 ModuleId 值是否在有效范围内 | Validate whether ModuleId value is within valid range
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">待验证的值 | Value to validate</param>
|
||||||
|
/// <returns>true 表示有效,false 表示无效 | true if valid, false if invalid</returns>
|
||||||
|
private static bool IsValidModuleId(ushort value)
|
||||||
|
{
|
||||||
|
return value >= 1 && value <= 65535;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证 LicenseState 值是否有效 | Validate whether LicenseState value is valid
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">待验证的值 | Value to validate</param>
|
||||||
|
/// <returns>true 表示有效,false 表示无效 | true if valid, false if invalid</returns>
|
||||||
|
private static bool IsValidLicenseState(int value)
|
||||||
|
{
|
||||||
|
foreach (var valid in ValidLicenseStates)
|
||||||
|
{
|
||||||
|
if (value == valid) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
namespace XP.Common.License.Configs
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 授权配置实体 | License configuration entity
|
||||||
|
/// </summary>
|
||||||
|
public class LicenseConfig
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 授权模式 | License mode
|
||||||
|
/// </summary>
|
||||||
|
public int LicenseMode { get; set; } = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 模块ID | Module ID
|
||||||
|
/// </summary>
|
||||||
|
public ushort ModuleId { get; set; } = 4;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否使用SMA | Whether to use SMA
|
||||||
|
/// </summary>
|
||||||
|
public bool UseSma { get; set; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 授权状态 | License state
|
||||||
|
/// </summary>
|
||||||
|
public int LicenseState { get; set; } = 20;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
namespace XP.Common.License.Enums;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 授权模式枚举 | License mode enumeration
|
||||||
|
/// </summary>
|
||||||
|
public enum LicenseMode : int
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// CLMS 正式授权 | CLMS formal authorization
|
||||||
|
/// </summary>
|
||||||
|
Clms = 0,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 临时测试模式(15分钟)| Temporary test mode (15 minutes)
|
||||||
|
/// </summary>
|
||||||
|
TemporaryTest = 885
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
namespace XP.Common.License.Enums;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 授权状态枚举 | License state enumeration
|
||||||
|
/// </summary>
|
||||||
|
public enum LicenseState : int
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 授权成功 | Authorization successful
|
||||||
|
/// </summary>
|
||||||
|
Success = 10,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 授权失败 | Authorization failed
|
||||||
|
/// </summary>
|
||||||
|
Fail = 20
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
using System;
|
||||||
|
using XP.Common.License.Enums;
|
||||||
|
|
||||||
|
namespace XP.Common.License.Implementations
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 授权检查结果(不可变)| License check result (immutable)
|
||||||
|
/// </summary>
|
||||||
|
public sealed class LicenseCheckResult
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 消息最大长度 | Maximum message length
|
||||||
|
/// </summary>
|
||||||
|
private const int MaxMessageLength = 512;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否授权成功 | Whether authorization is successful
|
||||||
|
/// </summary>
|
||||||
|
public bool IsAuthorized { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 结果消息(最大512字符)| Result message (maximum 512 characters)
|
||||||
|
/// </summary>
|
||||||
|
public string Message { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 授权模式 | License mode
|
||||||
|
/// </summary>
|
||||||
|
public LicenseMode LicenseMode { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 模块ID | Module ID
|
||||||
|
/// </summary>
|
||||||
|
public ushort ModuleId { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 授权到期日期 | License expiration date
|
||||||
|
/// </summary>
|
||||||
|
public DateTime? ExpirationDate { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// SMA到期日期 | SMA expiration date
|
||||||
|
/// </summary>
|
||||||
|
public DateTime? SmaDate { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 浮动许可IP地址 | Floating license IP address
|
||||||
|
/// </summary>
|
||||||
|
public string? FloatingLicenseIp { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 浮动许可端口 | Floating license port
|
||||||
|
/// </summary>
|
||||||
|
public string? FloatingLicensePort { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数 | Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="isAuthorized">是否授权成功 | Whether authorization is successful</param>
|
||||||
|
/// <param name="message">结果消息 | Result message</param>
|
||||||
|
/// <param name="licenseMode">授权模式 | License mode</param>
|
||||||
|
/// <param name="moduleId">模块ID | Module ID</param>
|
||||||
|
/// <param name="expirationDate">授权到期日期 | License expiration date</param>
|
||||||
|
/// <param name="smaDate">SMA到期日期 | SMA expiration date</param>
|
||||||
|
/// <param name="floatingLicenseIp">浮动许可IP地址 | Floating license IP address</param>
|
||||||
|
/// <param name="floatingLicensePort">浮动许可端口 | Floating license port</param>
|
||||||
|
public LicenseCheckResult(
|
||||||
|
bool isAuthorized,
|
||||||
|
string message,
|
||||||
|
LicenseMode licenseMode,
|
||||||
|
ushort moduleId,
|
||||||
|
DateTime? expirationDate,
|
||||||
|
DateTime? smaDate,
|
||||||
|
string? floatingLicenseIp,
|
||||||
|
string? floatingLicensePort)
|
||||||
|
{
|
||||||
|
IsAuthorized = isAuthorized;
|
||||||
|
Message = string.IsNullOrEmpty(message)
|
||||||
|
? string.Empty
|
||||||
|
: message.Length > MaxMessageLength
|
||||||
|
? message[..MaxMessageLength]
|
||||||
|
: message;
|
||||||
|
LicenseMode = licenseMode;
|
||||||
|
ModuleId = moduleId;
|
||||||
|
ExpirationDate = expirationDate;
|
||||||
|
SmaDate = smaDate;
|
||||||
|
FloatingLicenseIp = floatingLicenseIp;
|
||||||
|
FloatingLicensePort = floatingLicensePort;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,595 @@
|
|||||||
|
using System;
|
||||||
|
using System.Configuration;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using XP.Common.License.Configs;
|
||||||
|
using XP.Common.License.Enums;
|
||||||
|
using XP.Common.License.Interfaces;
|
||||||
|
using XP.Common.License.Native;
|
||||||
|
using XP.Common.Logging.Interfaces;
|
||||||
|
|
||||||
|
namespace XP.Common.License.Implementations
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 授权服务实现 | License service implementation
|
||||||
|
/// </summary>
|
||||||
|
public class LicenseService : ILicenseService, IDisposable
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 临时测试模式初始时间(秒)| Temporary test mode initial time (seconds)
|
||||||
|
/// </summary>
|
||||||
|
private const int TestModeInitialSeconds = 900;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 授权配置 | License configuration
|
||||||
|
/// </summary>
|
||||||
|
private readonly LicenseConfig _config;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 日志服务 | Logger service
|
||||||
|
/// </summary>
|
||||||
|
private readonly ILoggerService _logger;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 同步锁对象 | Synchronization lock object
|
||||||
|
/// </summary>
|
||||||
|
private readonly object _lock = new object();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 临时测试模式计时器 | Temporary test mode timer
|
||||||
|
/// </summary>
|
||||||
|
private Timer? _testModeTimer;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 临时测试模式剩余时间(秒)| Temporary test mode remaining time (seconds)
|
||||||
|
/// </summary>
|
||||||
|
private int _remainingTestSeconds = TestModeInitialSeconds;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 授权到期日期 | License expiration date
|
||||||
|
/// </summary>
|
||||||
|
private DateTime? _expirationDate;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// SMA到期日期 | SMA expiration date
|
||||||
|
/// </summary>
|
||||||
|
private DateTime? _smaDate;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 浮动许可IP地址 | Floating license IP address
|
||||||
|
/// </summary>
|
||||||
|
private string? _floatingLicenseIp;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 浮动许可端口 | Floating license port
|
||||||
|
/// </summary>
|
||||||
|
private string? _floatingLicensePort;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否已释放 | Whether disposed
|
||||||
|
/// </summary>
|
||||||
|
private bool _disposed;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数 | Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="config">授权配置 | License configuration</param>
|
||||||
|
/// <param name="logger">日志服务 | Logger service</param>
|
||||||
|
public LicenseService(LicenseConfig config, ILoggerService logger)
|
||||||
|
{
|
||||||
|
_config = config ?? throw new ArgumentNullException(nameof(config));
|
||||||
|
_logger = logger?.ForModule<LicenseService>() ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
LicenseMode = (LicenseMode)_config.LicenseMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当前会话是否已授权 | Whether the current session is authorized
|
||||||
|
/// </summary>
|
||||||
|
public bool IsAuthorized { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当前授权模式 | Current license mode
|
||||||
|
/// </summary>
|
||||||
|
public LicenseMode LicenseMode { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 临时测试模式超时事件(到期时触发)| Temporary test mode timeout event (fires when expired)
|
||||||
|
/// </summary>
|
||||||
|
public event EventHandler? TestModeTimeout;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 临时测试模式剩余5分钟警告事件 | Temporary test mode 5-minute warning event
|
||||||
|
/// </summary>
|
||||||
|
public event EventHandler? TestModeWarning5Min;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 临时测试模式剩余1分钟警告事件 | Temporary test mode 1-minute warning event
|
||||||
|
/// </summary>
|
||||||
|
public event EventHandler? TestModeWarning1Min;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否已触发5分钟警告 | Whether 5-minute warning has been fired
|
||||||
|
/// </summary>
|
||||||
|
private bool _warning5MinFired;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否已触发1分钟警告 | Whether 1-minute warning has been fired
|
||||||
|
/// </summary>
|
||||||
|
private bool _warning1MinFired;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 执行授权检查 | Perform authorization check
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>授权检查结果 | License check result</returns>
|
||||||
|
public LicenseCheckResult CheckAuthorization()
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
_logger.Info("开始授权检查,模式={Mode} | Starting authorization check, mode={Mode}", (int)LicenseMode);
|
||||||
|
|
||||||
|
if (LicenseMode == Enums.LicenseMode.TemporaryTest)
|
||||||
|
{
|
||||||
|
return HandleTemporaryTestMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
return HandleClmsAuthorization();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取授权到期日期 | Get license expiration date
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>授权到期日期,未授权时返回 null | License expiration date, null if not authorized</returns>
|
||||||
|
public DateTime? GetExpirationDate()
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
return _expirationDate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 检查模块是否授权 | Check if module is licensed
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="moduleId">模块ID | Module ID</param>
|
||||||
|
/// <returns>模块是否授权 | Whether the module is licensed</returns>
|
||||||
|
public bool IsModuleLicensed(ushort moduleId)
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
if (!IsAuthorized)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ushort mod = moduleId;
|
||||||
|
ushort type = 0;
|
||||||
|
return NativeMethods.CLM_ModuleIsLicensed(ref mod, ref type);
|
||||||
|
}
|
||||||
|
catch (DllNotFoundException ex)
|
||||||
|
{
|
||||||
|
_logger.Error(ex, "MORCODE.dll 加载失败 | Failed to load MORCODE.dll");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
catch (EntryPointNotFoundException ex)
|
||||||
|
{
|
||||||
|
_logger.Error(ex, "MORCODE.dll 中缺少入口点 | Missing entry point in MORCODE.dll");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取SMA到期日期 | Get SMA expiration date
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>SMA到期日期,未启用时返回 null | SMA expiration date, null if not enabled</returns>
|
||||||
|
public DateTime? GetSmaDate()
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
return _smaDate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取临时测试模式剩余时间 | Get remaining time in temporary test mode
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>剩余时间(秒),非测试模式返回 -1 | Remaining time in seconds, -1 if not in test mode</returns>
|
||||||
|
public int GetRemainingTestTime()
|
||||||
|
{
|
||||||
|
if (LicenseMode != Enums.LicenseMode.TemporaryTest)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return Interlocked.CompareExchange(ref _remainingTestSeconds, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 释放资源 | Release resources
|
||||||
|
/// </summary>
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_disposed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_disposed = true;
|
||||||
|
|
||||||
|
// 停止计时器 | Stop timer
|
||||||
|
_testModeTimer?.Dispose();
|
||||||
|
_testModeTimer = null;
|
||||||
|
|
||||||
|
// 尝试登出 | Try to logout
|
||||||
|
try
|
||||||
|
{
|
||||||
|
NativeMethods.CLM_Logout();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Error(ex, "CLM_Logout 调用失败 | CLM_Logout call failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 处理临时测试模式 | Handle temporary test mode
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>授权检查结果 | License check result</returns>
|
||||||
|
private LicenseCheckResult HandleTemporaryTestMode()
|
||||||
|
{
|
||||||
|
IsAuthorized = true;
|
||||||
|
_remainingTestSeconds = TestModeInitialSeconds;
|
||||||
|
|
||||||
|
// 启动计时器,每秒递减 | Start timer, decrement every second
|
||||||
|
_testModeTimer?.Dispose();
|
||||||
|
_testModeTimer = new Timer(TestModeTimerCallback, null, 1000, 1000);
|
||||||
|
|
||||||
|
_logger.Info("临时测试模式已启动,剩余时间={Seconds}秒 | Temporary test mode started, remaining time={Seconds}s", TestModeInitialSeconds);
|
||||||
|
|
||||||
|
WriteLicenseStateToConfig(LicenseState.Success);
|
||||||
|
|
||||||
|
return new LicenseCheckResult(
|
||||||
|
isAuthorized: true,
|
||||||
|
message: "临时测试模式已启动,有效时间15分钟 | Temporary test mode started, valid for 15 minutes",
|
||||||
|
licenseMode: Enums.LicenseMode.TemporaryTest,
|
||||||
|
moduleId: _config.ModuleId,
|
||||||
|
expirationDate: null,
|
||||||
|
smaDate: null,
|
||||||
|
floatingLicenseIp: null,
|
||||||
|
floatingLicensePort: null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 临时测试模式计时器回调 | Temporary test mode timer callback
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="state">状态对象 | State object</param>
|
||||||
|
private void TestModeTimerCallback(object? state)
|
||||||
|
{
|
||||||
|
int remaining = Interlocked.Decrement(ref _remainingTestSeconds);
|
||||||
|
|
||||||
|
// 剩余5分钟(300秒)时触发警告 | Fire warning at 5 minutes (300 seconds) remaining
|
||||||
|
if (remaining <= 300 && !_warning5MinFired)
|
||||||
|
{
|
||||||
|
_warning5MinFired = true;
|
||||||
|
_logger.Warn("临时测试模式剩余5分钟 | Temporary test mode: 5 minutes remaining");
|
||||||
|
TestModeWarning5Min?.Invoke(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 剩余1分钟(60秒)时触发警告 | Fire warning at 1 minute (60 seconds) remaining
|
||||||
|
if (remaining <= 60 && !_warning1MinFired)
|
||||||
|
{
|
||||||
|
_warning1MinFired = true;
|
||||||
|
_logger.Warn("临时测试模式剩余1分钟 | Temporary test mode: 1 minute remaining");
|
||||||
|
TestModeWarning1Min?.Invoke(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 到期时触发超时事件 | Fire timeout event when expired
|
||||||
|
if (remaining <= 0)
|
||||||
|
{
|
||||||
|
// 停止计时器 | Stop timer
|
||||||
|
_testModeTimer?.Dispose();
|
||||||
|
_testModeTimer = null;
|
||||||
|
|
||||||
|
_logger.Info("临时测试模式已超时 | Temporary test mode has timed out");
|
||||||
|
|
||||||
|
// 触发超时事件 | Raise timeout event
|
||||||
|
TestModeTimeout?.Invoke(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 处理 CLMS 正式授权流程 | Handle CLMS formal authorization flow
|
||||||
|
/// 兼容新旧版本 SDK:对可能不存在的入口点使用 TryInvoke 优雅降级 |
|
||||||
|
/// Compatible with old/new SDK: gracefully degrades for missing entry points via TryInvoke
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>授权检查结果 | License check result</returns>
|
||||||
|
private LicenseCheckResult HandleClmsAuthorization()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 步骤 1:检查系统时间(可选,老版本 SDK 可能不支持)| Step 1: Check system time (optional, old SDK may not support)
|
||||||
|
if (!TryInvokeOptional(() => NativeMethods.CLM_CheckSystemTime(), "CLM_CheckSystemTime", out bool checkTimeResult))
|
||||||
|
{
|
||||||
|
// 入口点不存在,跳过此步骤 | Entry point not found, skip this step
|
||||||
|
_logger.Warn("CLM_CheckSystemTime 入口点不存在,跳过系统时间检查(SDK版本较旧)| CLM_CheckSystemTime entry point not found, skipping system time check (older SDK version)");
|
||||||
|
}
|
||||||
|
else if (!checkTimeResult)
|
||||||
|
{
|
||||||
|
_logger.Error(new InvalidOperationException("CLM_CheckSystemTime"), "系统时间检查异常 | System time check anomaly");
|
||||||
|
return CreateFailureResult("系统时间检查异常 | System time check anomaly");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.Info("系统时间检查正常 | System time check: OK");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 步骤 2:登录验证(核心,必须存在)| Step 2: Login verification (core, must exist)
|
||||||
|
StringBuilder password = new StringBuilder("FnEoFWSNLpVeoNWYhVoHLfgITRvieSszJfylVsXOTsLkphgkPzPhbLQzQrvRbNOkVVIQyMWkyGVjWSaiYUEksfQsRmklksLxrmeTksKKNMoZoWfZeDaLDSyWwEmtQakvSNxBMBLHoLEZHtaoXNpTWiaUGaSLQdsHFZnbRyPehytarNTKpaNNqnjFNggqWifhFsrZasDsWbIGWDrhnGrdtUNDMjJdhlTunsssxCzYpsLQrWBxUkuUUEJraSbTlbuX");
|
||||||
|
if (!NativeMethods.CLM_Login(password))
|
||||||
|
{
|
||||||
|
_logger.Error(new InvalidOperationException("CLM_Login"), "CLM_Login 登录验证失败 | CLM_Login verification failed");
|
||||||
|
return CreateFailureResult("CLM_Login 登录验证失败 | CLM_Login verification failed");
|
||||||
|
}
|
||||||
|
_logger.Info("CLM_Login 登录验证成功 | CLM_Login verification: OK");
|
||||||
|
|
||||||
|
// 步骤 3:检查许可范围(核心,必须存在)| Step 3: Check license scope (core, must exist)
|
||||||
|
if (!NativeMethods.CLM_Login_Scope())
|
||||||
|
{
|
||||||
|
_logger.Error(new InvalidOperationException("CLM_Login_Scope"), "CLM_Login_Scope 许可范围检查失败 | CLM_Login_Scope scope check failed");
|
||||||
|
return CreateFailureResult("CLM_Login_Scope 许可范围检查失败 | CLM_Login_Scope scope check failed");
|
||||||
|
}
|
||||||
|
_logger.Info("CLM_Login_Scope 许可范围检查成功 | CLM_Login_Scope scope check: OK");
|
||||||
|
|
||||||
|
// 步骤 4:SMA 验证(如果启用,可选入口点)| Step 4: SMA validation (if enabled, optional entry point)
|
||||||
|
if (_config.UseSma)
|
||||||
|
{
|
||||||
|
var smaResult = ValidateSma();
|
||||||
|
if (smaResult != null)
|
||||||
|
return smaResult;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.Info("放弃检查SMA | SMA check skipped");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 步骤 5:获取浮动许可IP和端口(可选,老版本 SDK 可能不支持)| Step 5: Get floating license IP and port (optional, old SDK may not support)
|
||||||
|
StringBuilder ip = new StringBuilder(256);
|
||||||
|
StringBuilder port = new StringBuilder(256);
|
||||||
|
if (!TryInvokeOptional(() => NativeMethods.CLM_GetIP(ip, port), "CLM_GetIP", out bool getIpResult))
|
||||||
|
{
|
||||||
|
_logger.Warn("CLM_GetIP 入口点不存在,跳过浮动许可IP获取(SDK版本较旧)| CLM_GetIP entry point not found, skipping floating license IP retrieval (older SDK version)");
|
||||||
|
}
|
||||||
|
else if (!getIpResult)
|
||||||
|
{
|
||||||
|
_logger.Error(new InvalidOperationException("CLM_GetIP"), "CLM_GetIP 获取浮动许可IP失败 | CLM_GetIP floating license IP retrieval failed");
|
||||||
|
return CreateFailureResult("CLM_GetIP 获取浮动许可IP失败 | CLM_GetIP floating license IP retrieval failed");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_floatingLicenseIp = ip.ToString();
|
||||||
|
_floatingLicensePort = port.ToString();
|
||||||
|
_logger.Info("CLM_GetIP 成功: ip={Ip}/port={Port} | CLM_GetIP success: ip={Ip}/port={Port}", _floatingLicenseIp, _floatingLicensePort);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 步骤 6:获取错误信息(可选,老版本 SDK 可能不支持)| Step 6: Get error message (optional, old SDK may not support)
|
||||||
|
StringBuilder error = new StringBuilder(512);
|
||||||
|
if (!TryInvokeOptional(() => NativeMethods.CLM_GetError(error), "CLM_GetError", out bool getErrorResult))
|
||||||
|
{
|
||||||
|
_logger.Warn("CLM_GetError 入口点不存在,跳过错误信息获取(SDK版本较旧)| CLM_GetError entry point not found, skipping error retrieval (older SDK version)");
|
||||||
|
}
|
||||||
|
else if (!getErrorResult)
|
||||||
|
{
|
||||||
|
_logger.Error(new InvalidOperationException("CLM_GetError"), "CLM_GetError 获取错误信息失败 | CLM_GetError error retrieval failed");
|
||||||
|
return CreateFailureResult("CLM_GetError 获取错误信息失败 | CLM_GetError error retrieval failed");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.Info("CLM_GetError 成功: {Error} | CLM_GetError success: {Error}", error.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 步骤 7:检查模块授权(核心,必须存在)| Step 7: Check module authorization (core, must exist)
|
||||||
|
ushort moduleId = _config.ModuleId;
|
||||||
|
ushort type = 0;
|
||||||
|
if (!NativeMethods.CLM_ModuleIsLicensed(ref moduleId, ref type))
|
||||||
|
{
|
||||||
|
_logger.Error(new InvalidOperationException("CLM_ModuleIsLicensed"), "模块号码{ModuleId}不可用 | Module {ModuleId} unavailable", moduleId);
|
||||||
|
return CreateFailureResult($"模块号码{moduleId}不可用 | Module {moduleId} unavailable");
|
||||||
|
}
|
||||||
|
_logger.Info("模块号码{ModuleId}有效 | Module {ModuleId} available", moduleId);
|
||||||
|
|
||||||
|
// 步骤 8:获取授权到期日期(核心,必须存在)| Step 8: Get warranty expiration date (core, must exist)
|
||||||
|
int month = 0, day = 0, year = 0;
|
||||||
|
if (!NativeMethods.CLM_GetWarrantyExpiration(ref month, ref day, ref year))
|
||||||
|
{
|
||||||
|
_logger.Error(new InvalidOperationException("CLM_GetWarrantyExpiration"), "获取授权到期日期失败 | Failed to get warranty expiration date");
|
||||||
|
return CreateFailureResult("获取授权到期日期失败 | Failed to get warranty expiration date");
|
||||||
|
}
|
||||||
|
|
||||||
|
_expirationDate = new DateTime(year, month, day);
|
||||||
|
_logger.Info("CLM_GetWarrantyExpiration 成功: {Year}/{Month}/{Day} | CLM_GetWarrantyExpiration success: {Year}/{Month}/{Day}", year, month, day);
|
||||||
|
|
||||||
|
// 检查是否在30天内到期 | Check if expiring within 30 days
|
||||||
|
string warningMessage = string.Empty;
|
||||||
|
TimeSpan timeToExpiry = _expirationDate.Value - DateTime.Now;
|
||||||
|
if (timeToExpiry.Days <= 30)
|
||||||
|
{
|
||||||
|
warningMessage = $"软件授权将于{year}年{month}月{day}日到期,请尽快联系海克斯康 | Software license will expire on {year}-{month}-{day}, please contact Hexagon";
|
||||||
|
_logger.Warn(warningMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 授权成功 | Authorization successful
|
||||||
|
IsAuthorized = true;
|
||||||
|
WriteLicenseStateToConfig(LicenseState.Success);
|
||||||
|
|
||||||
|
string successMessage = string.IsNullOrEmpty(warningMessage)
|
||||||
|
? "授权检查成功 | Authorization check successful"
|
||||||
|
: $"授权检查成功(警告:{warningMessage})| Authorization check successful (Warning: {warningMessage})";
|
||||||
|
|
||||||
|
_logger.Info("授权检查完成 | Authorization check completed: Mode={Mode}, State={State}, Expiration={Expiration}",
|
||||||
|
(int)LicenseMode,
|
||||||
|
(int)LicenseState.Success,
|
||||||
|
_expirationDate?.ToString("yyyy-MM-dd") ?? "N/A");
|
||||||
|
|
||||||
|
return new LicenseCheckResult(
|
||||||
|
isAuthorized: true,
|
||||||
|
message: successMessage,
|
||||||
|
licenseMode: LicenseMode,
|
||||||
|
moduleId: _config.ModuleId,
|
||||||
|
expirationDate: _expirationDate,
|
||||||
|
smaDate: _smaDate,
|
||||||
|
floatingLicenseIp: _floatingLicenseIp,
|
||||||
|
floatingLicensePort: _floatingLicensePort);
|
||||||
|
}
|
||||||
|
catch (DllNotFoundException ex)
|
||||||
|
{
|
||||||
|
_logger.Error(ex, "MORCODE.dll 加载失败 | Failed to load MORCODE.dll");
|
||||||
|
return CreateFailureResult("CLMS SDK 不可用 | CLMS SDK unavailable");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 尝试调用可选的 SDK 方法,兼容老版本 SDK 中不存在的入口点 |
|
||||||
|
/// Try to invoke an optional SDK method, compatible with missing entry points in older SDK versions
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="action">要调用的方法委托 | Method delegate to invoke</param>
|
||||||
|
/// <param name="methodName">方法名称(用于日志)| Method name (for logging)</param>
|
||||||
|
/// <param name="result">方法返回值,入口点不存在时为 default | Method return value, default if entry point not found</param>
|
||||||
|
/// <returns>true: 入口点存在并已调用; false: 入口点不存在(EntryPointNotFoundException)| true: entry point exists and was invoked; false: entry point not found</returns>
|
||||||
|
private bool TryInvokeOptional(Func<bool> action, string methodName, out bool result)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
result = action();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (EntryPointNotFoundException)
|
||||||
|
{
|
||||||
|
result = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证 SMA | Validate SMA
|
||||||
|
/// 兼容老版本 SDK:CLM_GetSmaDate 入口点不存在时跳过 SMA 验证 |
|
||||||
|
/// Compatible with old SDK: skips SMA validation if CLM_GetSmaDate entry point not found
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>失败时返回失败结果,成功或跳过时返回 null | Returns failure result on failure, null on success or skip</returns>
|
||||||
|
private LicenseCheckResult? ValidateSma()
|
||||||
|
{
|
||||||
|
int yearSma = 0, monthSma = 0, daySma = 0;
|
||||||
|
|
||||||
|
// CLM_GetSmaDate 在老版本 SDK 中可能不存在 | CLM_GetSmaDate may not exist in older SDK
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!NativeMethods.CLM_GetSmaDate(ref monthSma, ref daySma, ref yearSma))
|
||||||
|
{
|
||||||
|
_logger.Error(new InvalidOperationException("CLM_GetSmaDate"), "检查SMA失败 | SMA check failed");
|
||||||
|
return CreateFailureResult("检查SMA失败 | SMA check failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (EntryPointNotFoundException)
|
||||||
|
{
|
||||||
|
_logger.Warn("CLM_GetSmaDate 入口点不存在,跳过SMA验证(SDK版本较旧)| CLM_GetSmaDate entry point not found, skipping SMA validation (older SDK version)");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.Info("CLM_GetSmaDate 成功: {Year}/{Month}/{Day} | CLM_GetSmaDate success: {Year}/{Month}/{Day}", yearSma, monthSma, daySma);
|
||||||
|
|
||||||
|
// 获取软件版本信息 | Get software version information
|
||||||
|
var version = Assembly.GetExecutingAssembly().GetName().Version;
|
||||||
|
int major = version?.Major ?? 0;
|
||||||
|
int minor = version?.Minor ?? 0;
|
||||||
|
|
||||||
|
// SMA 年份 < 软件主版本号 → 失败 | SMA year < software major version → failure
|
||||||
|
if (yearSma < major)
|
||||||
|
{
|
||||||
|
string msg = $"CLMS授权中SMA年份{yearSma}小于软件主版本号(年份){major},请联系海克斯康升级许可 | SMA year {yearSma} is less than software major version {major}, please contact Hexagon to upgrade license";
|
||||||
|
_logger.Error(new InvalidOperationException("SMA验证失败"), msg);
|
||||||
|
return CreateFailureResult(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// SMA 年份 == 软件主版本号时,校验季度 | When SMA year == software major version, validate quarter
|
||||||
|
if (yearSma == major)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
DateTime smaDate = new DateTime(yearSma, monthSma, daySma);
|
||||||
|
int smaQuarter = (smaDate.Month - 1) / 3 + 1;
|
||||||
|
|
||||||
|
if (minor > smaQuarter)
|
||||||
|
{
|
||||||
|
string msg = $"CLMS授权日期{yearSma}/{monthSma}/{daySma}属于{yearSma}年第{smaQuarter}季度,不支持当前{major}年第{minor}季度的软件版本 | SMA date {yearSma}/{monthSma}/{daySma} is in Q{smaQuarter} of {yearSma}, does not support current Q{minor} of {major} software version";
|
||||||
|
_logger.Error(new InvalidOperationException("SMA季度验证失败"), msg);
|
||||||
|
return CreateFailureResult(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
string msg = $"SMA授权日期{yearSma}/{monthSma}/{daySma}不合法 | SMA date {yearSma}/{monthSma}/{daySma} is invalid: {ex.Message}";
|
||||||
|
_logger.Error(ex, msg);
|
||||||
|
return CreateFailureResult(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_smaDate = new DateTime(yearSma, monthSma, daySma);
|
||||||
|
_logger.Info("SMA校验成功,SMA有效期至{Year}/{Month}/{Day} | SMA validation successful, valid until {Year}/{Month}/{Day}", yearSma, monthSma, daySma);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建失败结果 | Create failure result
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message">失败消息 | Failure message</param>
|
||||||
|
/// <returns>授权检查失败结果 | License check failure result</returns>
|
||||||
|
private LicenseCheckResult CreateFailureResult(string message)
|
||||||
|
{
|
||||||
|
IsAuthorized = false;
|
||||||
|
WriteLicenseStateToConfig(LicenseState.Fail);
|
||||||
|
|
||||||
|
_logger.Info("授权检查完成 | Authorization check completed: Mode={Mode}, State={State}, Expiration={Expiration}",
|
||||||
|
(int)LicenseMode,
|
||||||
|
(int)LicenseState.Fail,
|
||||||
|
"N/A");
|
||||||
|
|
||||||
|
return new LicenseCheckResult(
|
||||||
|
isAuthorized: false,
|
||||||
|
message: message,
|
||||||
|
licenseMode: LicenseMode,
|
||||||
|
moduleId: _config.ModuleId,
|
||||||
|
expirationDate: null,
|
||||||
|
smaDate: _smaDate,
|
||||||
|
floatingLicenseIp: _floatingLicenseIp,
|
||||||
|
floatingLicensePort: _floatingLicensePort);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 写入授权状态到配置文件 | Write license state to configuration file
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="state">授权状态 | License state</param>
|
||||||
|
private void WriteLicenseStateToConfig(LicenseState state)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
|
||||||
|
var settings = config.AppSettings.Settings;
|
||||||
|
|
||||||
|
if (settings["License:LicenseState"] == null)
|
||||||
|
{
|
||||||
|
settings.Add("License:LicenseState", ((int)state).ToString());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
settings["License:LicenseState"].Value = ((int)state).ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
config.Save(ConfigurationSaveMode.Modified);
|
||||||
|
ConfigurationManager.RefreshSection("appSettings");
|
||||||
|
|
||||||
|
_logger.Info("授权状态已写入配置: {State} | License state written to config: {State}", (int)state);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Error(ex, "写入授权状态到配置失败 | Failed to write license state to config");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
using System;
|
||||||
|
using XP.Common.License.Enums;
|
||||||
|
using XP.Common.License.Implementations;
|
||||||
|
|
||||||
|
namespace XP.Common.License.Interfaces;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 授权服务接口 | License service interface
|
||||||
|
/// </summary>
|
||||||
|
public interface ILicenseService
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 执行授权检查 | Perform authorization check
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>授权检查结果 | License check result</returns>
|
||||||
|
LicenseCheckResult CheckAuthorization();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当前会话是否已授权 | Whether the current session is authorized
|
||||||
|
/// </summary>
|
||||||
|
bool IsAuthorized { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取授权到期日期 | Get license expiration date
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>授权到期日期,未授权时返回 null | License expiration date, null if not authorized</returns>
|
||||||
|
DateTime? GetExpirationDate();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 检查模块是否授权 | Check if module is licensed
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="moduleId">模块ID | Module ID</param>
|
||||||
|
/// <returns>模块是否授权 | Whether the module is licensed</returns>
|
||||||
|
bool IsModuleLicensed(ushort moduleId);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取SMA到期日期 | Get SMA expiration date
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>SMA到期日期,未启用时返回 null | SMA expiration date, null if not enabled</returns>
|
||||||
|
DateTime? GetSmaDate();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当前授权模式 | Current license mode
|
||||||
|
/// </summary>
|
||||||
|
LicenseMode LicenseMode { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 临时测试模式超时事件(到期时触发,应用应执行正常关闭流程)| Temporary test mode timeout event (fires when expired, app should perform graceful shutdown)
|
||||||
|
/// </summary>
|
||||||
|
event EventHandler TestModeTimeout;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 临时测试模式剩余5分钟警告事件 | Temporary test mode 5-minute warning event
|
||||||
|
/// </summary>
|
||||||
|
event EventHandler TestModeWarning5Min;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 临时测试模式剩余1分钟警告事件 | Temporary test mode 1-minute warning event
|
||||||
|
/// </summary>
|
||||||
|
event EventHandler TestModeWarning1Min;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取临时测试模式剩余时间 | Get remaining time in temporary test mode
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>剩余时间(秒),非测试模式返回 0 | Remaining time in seconds, 0 if not in test mode</returns>
|
||||||
|
int GetRemainingTestTime();
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
using XP.Common.License.Enums;
|
||||||
|
|
||||||
|
namespace XP.Common.License.Native
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// CLMS SDK 原生方法封装 | CLMS SDK native methods encapsulation
|
||||||
|
/// </summary>
|
||||||
|
internal static class NativeMethods
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 登录验证 | Login verification
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="str">验证字符串 | Verification string</param>
|
||||||
|
/// <returns>TRUE: 成功 | TRUE: Success; FALSE: 失败 | FALSE: Failure</returns>
|
||||||
|
[DllImport("MORCODE.dll", EntryPoint = "CLM_Login", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
|
||||||
|
internal static extern bool CLM_Login(StringBuilder str);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 退出登录 | Logout
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>TRUE: 成功 | TRUE: Success; FALSE: 失败 | FALSE: Failure</returns>
|
||||||
|
[DllImport("MORCODE.dll", EntryPoint = "CLM_Logout", CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
internal static extern bool CLM_Logout();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 检查许可范围 | Check license scope
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>TRUE: 有许可 | TRUE: Has license; FALSE: 无许可 | FALSE: No license</returns>
|
||||||
|
[DllImport("MORCODE.dll", EntryPoint = "CLM_Login_Scope", CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
internal static extern bool CLM_Login_Scope();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 检查模块是否授权 | Check if module is licensed
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mod">模块ID | Module ID</param>
|
||||||
|
/// <param name="type">类型(暂无定义)| Type (undefined)</param>
|
||||||
|
/// <returns>TRUE: 模块可用 | TRUE: Module available; FALSE: 模块不可用 | FALSE: Module unavailable</returns>
|
||||||
|
[DllImport("MORCODE.dll", EntryPoint = "CLM_ModuleIsLicensed", CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
internal static extern bool CLM_ModuleIsLicensed(ref ushort mod, ref ushort type);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取保修到期日期 | Get warranty expiration date
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mon">月份 | Month</param>
|
||||||
|
/// <param name="day">日期 | Day</param>
|
||||||
|
/// <param name="year">年份 | Year</param>
|
||||||
|
/// <returns>TRUE: 成功 | TRUE: Success; FALSE: 失败 | FALSE: Failure</returns>
|
||||||
|
[DllImport("MORCODE.dll", EntryPoint = "CLM_GetWarrantyExpiration", CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
internal static extern bool CLM_GetWarrantyExpiration(ref int mon, ref int day, ref int year);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取浮动许可的IP地址和端口 | Get floating license IP and port
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ip">IP地址 | IP address</param>
|
||||||
|
/// <param name="port">端口 | Port</param>
|
||||||
|
/// <returns>TRUE: 成功 | TRUE: Success; FALSE: 失败 | FALSE: Failure</returns>
|
||||||
|
[DllImport("MORCODE.dll", EntryPoint = "CLM_GetIP", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
|
||||||
|
internal static extern bool CLM_GetIP(StringBuilder ip, StringBuilder port);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取错误信息 | Get error message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="error">错误信息 | Error message</param>
|
||||||
|
/// <returns>TRUE: 成功 | TRUE: Success; FALSE: 失败 | FALSE: Failure</returns>
|
||||||
|
[DllImport("MORCODE.dll", EntryPoint = "CLM_GetError", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
|
||||||
|
internal static extern bool CLM_GetError(StringBuilder error);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 检查系统时间 | Check system time
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>TRUE: 系统时间正常 | TRUE: System time normal; FALSE: 系统时间异常 | FALSE: System time anomaly</returns>
|
||||||
|
[DllImport("MORCODE.dll", EntryPoint = "CLM_CheckSystemTime", CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
internal static extern bool CLM_CheckSystemTime();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取SmartService信息 | Get SmartService information
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ControllerId">控制器ID | Controller ID</param>
|
||||||
|
/// <param name="UserName">用户名 | User name</param>
|
||||||
|
/// <returns>TRUE: 成功 | TRUE: Success; FALSE: 失败 | FALSE: Failure</returns>
|
||||||
|
[DllImport("MORCODE.dll", EntryPoint = "CLM_SmartService", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
|
||||||
|
internal static extern bool CLM_SmartService(StringBuilder ControllerId, StringBuilder UserName);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取SMA日期 | Get SMA date
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mon">月份 | Month</param>
|
||||||
|
/// <param name="day">日期 | Day</param>
|
||||||
|
/// <param name="year">年份 | Year</param>
|
||||||
|
/// <returns>TRUE: 成功 | TRUE: Success; FALSE: 失败 | FALSE: Failure</returns>
|
||||||
|
[DllImport("MORCODE.dll", EntryPoint = "CLM_GetSmaDate", CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
internal static extern bool CLM_GetSmaDate(ref int mon, ref int day, ref int year);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,8 @@ using Prism.Modularity;
|
|||||||
using XP.Common.Dump.Configs;
|
using XP.Common.Dump.Configs;
|
||||||
using XP.Common.Dump.Implementations;
|
using XP.Common.Dump.Implementations;
|
||||||
using XP.Common.Dump.Interfaces;
|
using XP.Common.Dump.Interfaces;
|
||||||
using XP.Common.Helpers;
|
using XP.Common.License.Configs;
|
||||||
|
using XP.Common.License.Interfaces;
|
||||||
using XP.Common.Localization.Configs;
|
using XP.Common.Localization.Configs;
|
||||||
using XP.Common.Localization.Extensions;
|
using XP.Common.Localization.Extensions;
|
||||||
using XP.Common.Localization.Implementations;
|
using XP.Common.Localization.Implementations;
|
||||||
@@ -12,6 +13,8 @@ using XP.Common.Localization.Interfaces;
|
|||||||
using XP.Common.Logging.Interfaces;
|
using XP.Common.Logging.Interfaces;
|
||||||
using XP.Common.PdfViewer.Implementations;
|
using XP.Common.PdfViewer.Implementations;
|
||||||
using XP.Common.PdfViewer.Interfaces;
|
using XP.Common.PdfViewer.Interfaces;
|
||||||
|
using DumpConfigLoader = XP.Common.Dump.Configs.ConfigLoader;
|
||||||
|
using LicenseConfigLoader = XP.Common.License.Configs.ConfigLoader;
|
||||||
|
|
||||||
namespace XP.Common.Module
|
namespace XP.Common.Module
|
||||||
{
|
{
|
||||||
@@ -65,7 +68,7 @@ namespace XP.Common.Module
|
|||||||
containerRegistry.RegisterSingleton<ILocalizationService, ResxLocalizationService>();
|
containerRegistry.RegisterSingleton<ILocalizationService, ResxLocalizationService>();
|
||||||
|
|
||||||
// 注册 Dump 配置为单例(通过工厂方法加载)| Register Dump config as singleton (via factory method)
|
// 注册 Dump 配置为单例(通过工厂方法加载)| Register Dump config as singleton (via factory method)
|
||||||
containerRegistry.RegisterSingleton<DumpConfig>(() => ConfigLoader.LoadDumpConfig());
|
containerRegistry.RegisterSingleton<DumpConfig>(() => DumpConfigLoader.LoadDumpConfig());
|
||||||
|
|
||||||
// 注册 Dump 服务为单例 | Register Dump service as singleton
|
// 注册 Dump 服务为单例 | Register Dump service as singleton
|
||||||
containerRegistry.RegisterSingleton<IDumpService, DumpService>();
|
containerRegistry.RegisterSingleton<IDumpService, DumpService>();
|
||||||
@@ -75,6 +78,12 @@ namespace XP.Common.Module
|
|||||||
|
|
||||||
// 注册 PDF 查看服务为单例 | Register PDF viewer service as singleton
|
// 注册 PDF 查看服务为单例 | Register PDF viewer service as singleton
|
||||||
containerRegistry.RegisterSingleton<IPdfViewerService, PdfViewerService>();
|
containerRegistry.RegisterSingleton<IPdfViewerService, PdfViewerService>();
|
||||||
|
|
||||||
|
// 注册授权配置为单例(通过工厂方法加载)| Register license config as singleton (via factory method)
|
||||||
|
containerRegistry.RegisterSingleton<LicenseConfig>(() => LicenseConfigLoader.LoadLicenseConfig());
|
||||||
|
|
||||||
|
// 注册授权服务为单例 | Register license service as singleton
|
||||||
|
containerRegistry.RegisterSingleton<ILicenseService, XP.Common.License.Implementations.LicenseService>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Binary file not shown.
@@ -62,4 +62,7 @@
|
|||||||
<DependentUpon>Resources.resx</DependentUpon>
|
<DependentUpon>Resources.resx</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Remove="License\Demo.cs" />
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,7 @@ using Serilog;
|
|||||||
using System;
|
using System;
|
||||||
using System.Resources;
|
using System.Resources;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using XP.Common.Configs;
|
using XP.Common.Logging.Configs;
|
||||||
using XP.Common.Localization;
|
using XP.Common.Localization;
|
||||||
using XP.Common.Localization.Interfaces;
|
using XP.Common.Localization.Interfaces;
|
||||||
using XP.Common.Logging;
|
using XP.Common.Logging;
|
||||||
@@ -118,7 +118,7 @@ namespace XP.Hardware.PLC.Sentry
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 加载并初始化 Serilog | Load and initialize Serilog
|
// 加载并初始化 Serilog | Load and initialize Serilog
|
||||||
SerilogConfig serilogConfig = XP.Common.Helpers.ConfigLoader.LoadSerilogConfig();
|
SerilogConfig serilogConfig = XP.Common.Logging.Configs.ConfigLoader.LoadSerilogConfig();
|
||||||
SerilogInitializer.Initialize(serilogConfig);
|
SerilogInitializer.Initialize(serilogConfig);
|
||||||
|
|
||||||
_logger.Information("PLC Sentry Monitor 启动开始 | PLC Sentry Monitor startup started");
|
_logger.Information("PLC Sentry Monitor 启动开始 | PLC Sentry Monitor startup started");
|
||||||
|
|||||||
+3
-3
@@ -2,11 +2,11 @@ using System;
|
|||||||
using System.Windows;
|
using System.Windows;
|
||||||
using Prism.Container.DryIoc;
|
using Prism.Container.DryIoc;
|
||||||
using Prism.Ioc;
|
using Prism.Ioc;
|
||||||
using XP.Common.Configs;
|
|
||||||
using XP.Common.Dump.Configs;
|
using XP.Common.Dump.Configs;
|
||||||
using XP.Common.Dump.Implementations;
|
using XP.Common.Dump.Implementations;
|
||||||
using XP.Common.Dump.Interfaces;
|
using XP.Common.Dump.Interfaces;
|
||||||
using XP.Common.Helpers;
|
using XP.Common.Logging.Configs;
|
||||||
|
using DumpConfigLoader = XP.Common.Dump.Configs.ConfigLoader;
|
||||||
using XP.Common.Localization.Configs;
|
using XP.Common.Localization.Configs;
|
||||||
using XP.Common.Localization.Extensions;
|
using XP.Common.Localization.Extensions;
|
||||||
using XP.Common.Localization;
|
using XP.Common.Localization;
|
||||||
@@ -71,7 +71,7 @@ namespace XP.Scan
|
|||||||
containerRegistry.RegisterSingleton<ILocalizationService, ResxLocalizationService>();
|
containerRegistry.RegisterSingleton<ILocalizationService, ResxLocalizationService>();
|
||||||
|
|
||||||
// Dump 服务
|
// Dump 服务
|
||||||
containerRegistry.RegisterSingleton<DumpConfig>(() => ConfigLoader.LoadDumpConfig());
|
containerRegistry.RegisterSingleton<DumpConfig>(() => DumpConfigLoader.LoadDumpConfig());
|
||||||
containerRegistry.RegisterSingleton<IDumpService, DumpService>();
|
containerRegistry.RegisterSingleton<IDumpService, DumpService>();
|
||||||
|
|
||||||
// 注册 XPScanView 用于区域导航
|
// 注册 XPScanView 用于区域导航
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using XP.Common.Configs;
|
using XP.Common.Database.Configs;
|
||||||
using XP.Common.Database.Implementations;
|
using XP.Common.Database.Implementations;
|
||||||
using XP.Common.Database.Interfaces;
|
using XP.Common.Database.Interfaces;
|
||||||
using XP.Common.Logging.Interfaces;
|
using XP.Common.Logging.Interfaces;
|
||||||
|
|||||||
@@ -11,6 +11,11 @@
|
|||||||
<add key="UserManual" value="UserManual.pdf" />
|
<add key="UserManual" value="UserManual.pdf" />
|
||||||
<add key="DeviceId" value="PlanerCT001" />
|
<add key="DeviceId" value="PlanerCT001" />
|
||||||
|
|
||||||
|
<!-- 授权配置 | License configuration -->
|
||||||
|
<add key="License:LicenseMode" value="0" />
|
||||||
|
<add key="License:ModuleId" value="4" />
|
||||||
|
<add key="License:UseSma" value="false" />
|
||||||
|
<add key="License:LicenseState" value="20" />
|
||||||
|
|
||||||
<!-- Serilog日志配置 -->
|
<!-- Serilog日志配置 -->
|
||||||
<add key="Serilog:LogPath" value="D:\XplorePlane\Logs" />
|
<add key="Serilog:LogPath" value="D:\XplorePlane\Logs" />
|
||||||
|
|||||||
+113
-5
@@ -10,14 +10,18 @@ using System.Threading;
|
|||||||
using System.Windows;
|
using System.Windows;
|
||||||
using Telerik.Windows.Controls;
|
using Telerik.Windows.Controls;
|
||||||
using XP.Camera;
|
using XP.Camera;
|
||||||
using XP.Common.Configs;
|
using XP.Common.Database.Configs;
|
||||||
using XP.Common.Database.Implementations;
|
using XP.Common.Database.Implementations;
|
||||||
using XP.Common.Database.Interfaces;
|
using XP.Common.Database.Interfaces;
|
||||||
using XP.Common.Dump.Configs;
|
using XP.Common.Dump.Configs;
|
||||||
using XP.Common.Dump.Implementations;
|
using XP.Common.Dump.Implementations;
|
||||||
using XP.Common.Dump.Interfaces;
|
using XP.Common.Dump.Interfaces;
|
||||||
using XP.Common.GeneralForm.Views;
|
using XP.Common.GeneralForm.Views;
|
||||||
using XP.Common.Helpers;
|
using XP.Common.Logging.Configs;
|
||||||
|
using DumpConfigLoader = XP.Common.Dump.Configs.ConfigLoader;
|
||||||
|
using LoggingConfigLoader = XP.Common.Logging.Configs.ConfigLoader;
|
||||||
|
using DatabaseConfigLoader = XP.Common.Database.Configs.ConfigLoader;
|
||||||
|
using XP.Common.License.Interfaces;
|
||||||
using XP.Common.Localization.Configs;
|
using XP.Common.Localization.Configs;
|
||||||
using XP.Common.Localization.Extensions;
|
using XP.Common.Localization.Extensions;
|
||||||
using XP.Common.Localization.Implementations;
|
using XP.Common.Localization.Implementations;
|
||||||
@@ -110,7 +114,7 @@ namespace XplorePlane
|
|||||||
private void ConfigureLogging()
|
private void ConfigureLogging()
|
||||||
{
|
{
|
||||||
// 加载 Serilog 配置 | Load Serilog configuration
|
// 加载 Serilog 配置 | Load Serilog configuration
|
||||||
SerilogConfig serilogConfig = ConfigLoader.LoadSerilogConfig();
|
SerilogConfig serilogConfig = LoggingConfigLoader.LoadSerilogConfig();
|
||||||
// 初始化 Serilog(全局唯一)| Initialize Serilog (global singleton)
|
// 初始化 Serilog(全局唯一)| Initialize Serilog (global singleton)
|
||||||
SerilogInitializer.Initialize(serilogConfig);
|
SerilogInitializer.Initialize(serilogConfig);
|
||||||
|
|
||||||
@@ -315,6 +319,13 @@ namespace XplorePlane
|
|||||||
_modulesInitialized = true;
|
_modulesInitialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 执行授权检查 | Perform license authorization check
|
||||||
|
if (!PerformLicenseCheck())
|
||||||
|
{
|
||||||
|
Application.Current.Shutdown();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
var shell = Container.Resolve<MainWindow>();
|
var shell = Container.Resolve<MainWindow>();
|
||||||
|
|
||||||
// 主窗口加载完成后再连接相机,确保所有模块和原生 DLL 已完成初始化
|
// 主窗口加载完成后再连接相机,确保所有模块和原生 DLL 已完成初始化
|
||||||
@@ -353,6 +364,103 @@ namespace XplorePlane
|
|||||||
return shell;
|
return shell;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 执行授权检查,授权失败时显示错误消息 | Perform license check, show error message on failure
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>授权是否成功 | Whether authorization succeeded</returns>
|
||||||
|
private bool PerformLicenseCheck()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var licenseService = Container.Resolve<ILicenseService>();
|
||||||
|
var result = licenseService.CheckAuthorization();
|
||||||
|
|
||||||
|
if (!result.IsAuthorized)
|
||||||
|
{
|
||||||
|
Log.Error("授权检查失败 | Authorization check failed: {Message}", result.Message);
|
||||||
|
MessageBox.Show(
|
||||||
|
result.Message,
|
||||||
|
"授权失败 | Authorization Failed",
|
||||||
|
MessageBoxButton.OK,
|
||||||
|
MessageBoxImage.Error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 订阅临时测试模式事件 | Subscribe to temporary test mode events
|
||||||
|
licenseService.TestModeWarning5Min += OnTestModeWarning5Min;
|
||||||
|
licenseService.TestModeWarning1Min += OnTestModeWarning1Min;
|
||||||
|
licenseService.TestModeTimeout += OnTestModeTimeout;
|
||||||
|
|
||||||
|
// 临时测试模式启动提示 | Temporary test mode startup notification
|
||||||
|
if (licenseService.LicenseMode == XP.Common.License.Enums.LicenseMode.TemporaryTest)
|
||||||
|
{
|
||||||
|
MessageBox.Show(
|
||||||
|
"当前为临时测试模式,软件将在15分钟后自动关闭。\nCurrently in temporary test mode, the software will automatically shut down after 15 minutes.",
|
||||||
|
"临时测试模式 | Temporary Test Mode",
|
||||||
|
MessageBoxButton.OK,
|
||||||
|
MessageBoxImage.Information);
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.Information("授权检查通过 | Authorization check passed, Mode={Mode}", licenseService.LicenseMode);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Error(ex, "授权检查过程中发生异常 | Exception during authorization check");
|
||||||
|
MessageBox.Show(
|
||||||
|
$"授权检查异常 | Authorization check exception: {ex.Message}",
|
||||||
|
"授权失败 | Authorization Failed",
|
||||||
|
MessageBoxButton.OK,
|
||||||
|
MessageBoxImage.Error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 处理临时测试模式剩余5分钟警告 | Handle temporary test mode 5-minute warning
|
||||||
|
/// </summary>
|
||||||
|
private void OnTestModeWarning5Min(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
Application.Current.Dispatcher.Invoke(() =>
|
||||||
|
{
|
||||||
|
Log.Warning("临时测试模式剩余5分钟 | Temporary test mode: 5 minutes remaining");
|
||||||
|
MessageBox.Show(
|
||||||
|
"临时测试模式将在5分钟后到期,请尽快保存您的工作。\nTemporary test mode will expire in 5 minutes, please save your work.",
|
||||||
|
"测试模式提醒 | Test Mode Reminder",
|
||||||
|
MessageBoxButton.OK,
|
||||||
|
MessageBoxImage.Information);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 处理临时测试模式剩余1分钟警告 | Handle temporary test mode 1-minute warning
|
||||||
|
/// </summary>
|
||||||
|
private void OnTestModeWarning1Min(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
Application.Current.Dispatcher.Invoke(() =>
|
||||||
|
{
|
||||||
|
Log.Warning("临时测试模式剩余1分钟 | Temporary test mode: 1 minute remaining");
|
||||||
|
MessageBox.Show(
|
||||||
|
"临时测试模式将在1分钟后到期,软件即将关闭,请立即保存您的工作。\nTemporary test mode will expire in 1 minute, the software will shut down soon. Please save your work immediately.",
|
||||||
|
"测试模式即将到期 | Test Mode Expiring",
|
||||||
|
MessageBoxButton.OK,
|
||||||
|
MessageBoxImage.Warning);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 处理临时测试模式超时事件(执行正常关闭流程)| Handle temporary test mode timeout event (perform graceful shutdown)
|
||||||
|
/// </summary>
|
||||||
|
private void OnTestModeTimeout(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
Application.Current.Dispatcher.Invoke(() =>
|
||||||
|
{
|
||||||
|
Log.Warning("临时测试模式已超时,执行正常关闭流程 | Temporary test mode timed out, performing graceful shutdown");
|
||||||
|
// 使用正常关闭流程,确保资源正确释放 | Use graceful shutdown to ensure proper resource release
|
||||||
|
Application.Current.MainWindow?.Close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 在主线程上检索并连接导航相机。
|
/// 在主线程上检索并连接导航相机。
|
||||||
/// pylon SDK 要求在主线程(STA)上操作,不能放到后台线程。
|
/// pylon SDK 要求在主线程(STA)上操作,不能放到后台线程。
|
||||||
@@ -428,14 +536,14 @@ namespace XplorePlane
|
|||||||
containerRegistry.Register<XP.Hardware.RaySource.ViewModels.RaySourceOperateViewModel>();
|
containerRegistry.Register<XP.Hardware.RaySource.ViewModels.RaySourceOperateViewModel>();
|
||||||
|
|
||||||
// 注册 SQLite 配置和数据库上下文(FilamentLifetimeService 依赖)
|
// 注册 SQLite 配置和数据库上下文(FilamentLifetimeService 依赖)
|
||||||
var sqliteConfig = XP.Common.Helpers.ConfigLoader.LoadSqliteConfig();
|
var sqliteConfig = DatabaseConfigLoader.LoadSqliteConfig();
|
||||||
containerRegistry.RegisterInstance(sqliteConfig);
|
containerRegistry.RegisterInstance(sqliteConfig);
|
||||||
containerRegistry.RegisterSingleton<IDbContext, SqliteContext>();
|
containerRegistry.RegisterSingleton<IDbContext, SqliteContext>();
|
||||||
|
|
||||||
// 注册通用模块的服务(本地化、Dump)
|
// 注册通用模块的服务(本地化、Dump)
|
||||||
containerRegistry.RegisterSingleton<ILocalizationConfig, LocalizationConfig>();
|
containerRegistry.RegisterSingleton<ILocalizationConfig, LocalizationConfig>();
|
||||||
containerRegistry.RegisterSingleton<ILocalizationService, ResxLocalizationService>();
|
containerRegistry.RegisterSingleton<ILocalizationService, ResxLocalizationService>();
|
||||||
containerRegistry.RegisterSingleton<DumpConfig>(() => XP.Common.Helpers.ConfigLoader.LoadDumpConfig());
|
containerRegistry.RegisterSingleton<DumpConfig>(() => DumpConfigLoader.LoadDumpConfig());
|
||||||
containerRegistry.RegisterSingleton<IDumpService, DumpService>();
|
containerRegistry.RegisterSingleton<IDumpService, DumpService>();
|
||||||
|
|
||||||
// ── CNC / 矩阵编排 / 测量数据服务(单例)──
|
// ── CNC / 矩阵编排 / 测量数据服务(单例)──
|
||||||
|
|||||||
Reference in New Issue
Block a user