using Edge.Core.Parser; using Edge.Core.Parser.BinaryParser.Util; using Edge.Core.Processor; using Edge.Core.Processor.Communicator; using Edge.Core.Processor.Dispatcher.Attributes; using Edge.Core.UniversalApi; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using ShengJu_CUT100_DES.MessageEntity.Incoming; using ShengJu_CUT100_DES.MessageEntity.Outgoing; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Timers; using static ShengJu_CUT100_DES.MessageEntity.Outgoing.ModifyPredefinedFileKey; namespace ShengJu_CUT100_DES { /// /// at least at 2021.4, 这模块还不支持并线,因为模块内部并没有区分请求中的地址值。 /// [MetaPartsRequired(typeof(HalfDuplexActivePollingDeviceProcessor<,>))] [MetaPartsRequired(typeof(ComPortCommunicator<>))] [MetaPartsRequired(typeof(TcpClientCommunicator<>))] [MetaPartsDescriptor( "lang-zh-cn:ShengJu CUT100_DES 感应卡读写卡模块lang-en-us:ShengJu CUT100_DES contactless card read&write module", "lang-zh-cn:用于驱动 盛炬CUT100_DES 感应卡读写卡模块, 波特率19200, None, 8, 1lang-en-us:Used for driven ShengJu CUT100_DES contactless card read&write module, BaudRate 19200, None, 8, 1", new[] { "lang-zh-cn:支付终端lang-en-us:Terminal" })] [UniversalApi(Name = GenericAlarm.UniversalApiEventName, EventDataType = typeof(GenericAlarm[]), Description = "Fire GenericAlarms to AlarmBar for attracting users.")] [UniversalApi(Name = OnCardTapEventName, EventDataType = typeof(object), Description = "Fired once a DesFire EV1 card detected by module and card UID is read")] public class GroupHandler : TestableActivePollingDeviceHandler { public const string OnCardTapEventName = "OnCardTap"; public event EventHandler OnModuleStateChange; public event EventHandler OnCardTap; private ILogger logger = NullLogger.Instance; private IServiceProvider services; protected IContext context; private DeviceConfigV1 deviceConfig; private IEnumerable cardReaderModules; private const int singleModuleOfflineTimeThresholdByMs = 15000; //private Dictionary boardsLastImcomingMsgReceivedTimes; private Timer singleModuleOfflineCheckTimer; [UniversalApi] public IEnumerable CardReaderModuels => this.cardReaderModules; public class CardReaderModule { private ILogger logger = NullLogger.Instance; private IContext context; public enum CardReaderModuleStateEnum { Offline, Online } public string Name { get; set; } public string Description { get; set; } public byte PhysicalAddress { get; } public string HardwareFirmware { get; set; } public string UnderlyingCommunicatorIdentity { get; set; } public CardReaderModuleStateEnum ModuleState { get; set; } /// /// used for track the last received message which for caculate the offline state of this device. /// public DateTime? LastIncomingMessageReceivedTime { get; set; } internal CardReaderModule(byte physicalAddress, ILogger logger) { this.PhysicalAddress = physicalAddress; this.logger = logger; } internal void SetContext(IContext context) { this.context = context; } internal async Task ReadCardUID(byte modulePhysicalAddress) { var readCardUidResponse = await this.context.Outgoing.WriteAsync( new ActivateATypeCardRequest() { ModulePhysicalAddress = modulePhysicalAddress }, (_, testResponse) => testResponse.ModulePhysicalAddress == modulePhysicalAddress && (testResponse is GenericFailureResponse || testResponse is ActivateATypeCardResponse), 4000); if (readCardUidResponse is ActivateATypeCardResponse rp) { if (rp.UID.Any()) return new CardOperationResult(200, rp.UID) { CardReaderModule = this }; else return new CardOperationResult(404) { CardReaderModule = this, ModuleResultCode = "No UID was read" }; } else if (readCardUidResponse is GenericFailureResponse failedResponse) { this.logger.LogInformation($"ReadCardUID failed with module code: {failedResponse.模块返回状态}, cpu code: {failedResponse.CpuCardState} for module with physical address: {failedResponse.ModulePhysicalAddress}"); return new CardOperationResult(400) { CardReaderModule = this, ModuleResultCode = failedResponse.模块返回状态.ToString(), CpuCardResultCode = failedResponse.CpuCardState.ToString() }; } else { this.logger.LogInformation($"ReadCardUID timed out for module (phyAdrs: {modulePhysicalAddress}, comm: {this.context.Communicator.Identity})"); return new CardOperationResult(500) { CardReaderModule = this, ModuleResultCode = "Device response timed out" }; } } internal async Task FormatCardByResetToNewRootKeyAndBuildDefaultFileStructures(byte modulePhysicalAddress, byte[] oldRootKeyBytes, byte[] newRootKeyBytes) { if (oldRootKeyBytes == null || !oldRootKeyBytes.Any()) throw new ArgumentException(nameof(oldRootKeyBytes)); if (newRootKeyBytes == null || !newRootKeyBytes.Any()) throw new ArgumentException(nameof(newRootKeyBytes)); var formatCardResponse = await this.context.Outgoing.WriteAsync( new FormatCardRequest(oldRootKeyBytes, newRootKeyBytes) { ModulePhysicalAddress = modulePhysicalAddress }, (_, testResponse) => testResponse.ModulePhysicalAddress == modulePhysicalAddress && (testResponse is GenericFailureResponse || testResponse is GenericSuccessResponse), 4000); if (formatCardResponse is GenericSuccessResponse) { this.logger.LogInformation($"Format Card successfully on module (phyAdrs: {modulePhysicalAddress}, comm: {this.context.Communicator.Identity}), will auto re-read card app dirs..."); var readAppDirsResponse = await this.context.Outgoing.WriteAsync( new ReadAppDirsRequest(newRootKeyBytes) { ModulePhysicalAddress = modulePhysicalAddress }, (_, testResponse) => testResponse.ModulePhysicalAddress == modulePhysicalAddress && (testResponse is GenericFailureResponse || testResponse is ReadAppDirsResponse), 4000); if (readAppDirsResponse is ReadAppDirsResponse successReadAppDirsResponse) { this.logger.LogInformation($" Read Card AppDirs, count: {successReadAppDirsResponse.DirCount}, " + $"dirIds: {successReadAppDirsResponse.DirIds.Select(did => did.Select(i => i.ToString("X2")).Aggregate("0x", (acc, n) => acc + " " + n)).Aggregate((acc, n) => acc + ", " + n)} " + $"on module (phyAdrs: {successReadAppDirsResponse.ModulePhysicalAddress}, comm: {this.context.Communicator.Identity})"); } else if (readAppDirsResponse is GenericFailureResponse failedReadAppDirsResponse) { this.logger.LogInformation($" Read Card AppDirs failed with module code: {failedReadAppDirsResponse.模块返回状态}, cpu code: {failedReadAppDirsResponse.CpuCardState} for module (phyAdrs: {failedReadAppDirsResponse.ModulePhysicalAddress}, comm: {this.context.Communicator.Identity})"); } else { this.logger.LogInformation($" Read Card AppDirs timed out for module (phyAdrs: {modulePhysicalAddress}, comm: {this.context.Communicator.Identity})"); } return new CardOperationResult(200) { CardReaderModule = this }; } else if (formatCardResponse is GenericFailureResponse failedFormatCardResponse) { this.logger.LogInformation($"Format Card failed with module code: {failedFormatCardResponse.模块返回状态}, cpu code: {failedFormatCardResponse.CpuCardState} for module with physical address: {failedFormatCardResponse.ModulePhysicalAddress}"); return new CardOperationResult(400) { CardReaderModule = this, ModuleResultCode = failedFormatCardResponse.模块返回状态.ToString(), CpuCardResultCode = failedFormatCardResponse.CpuCardState.ToString() }; } else { this.logger.LogInformation($"Format Card timed out for module with physical address: {modulePhysicalAddress}"); return new CardOperationResult(500) { CardReaderModule = this, ModuleResultCode = "Device response timed out" }; } } internal async Task WriteCard(byte modulePhysicalAddress, byte fileId, byte blockAddress, byte[] dataBytes, byte[] writeKeyBytes) { if (writeKeyBytes == null || !writeKeyBytes.Any()) throw new ArgumentException(nameof(writeKeyBytes)); var writeAppFileRequestResponse = await this.context.Outgoing.WriteAsync( new WriteAppFileRequest(fileId, blockAddress, writeKeyBytes, dataBytes) { ModulePhysicalAddress = modulePhysicalAddress }, (_, testResponse) => testResponse.ModulePhysicalAddress == modulePhysicalAddress && (testResponse is GenericFailureResponse || testResponse is GenericSuccessResponse), 4000); if (writeAppFileRequestResponse is GenericSuccessResponse successResponse) { return new CardOperationResult(200) { CardReaderModule = this }; } else if (writeAppFileRequestResponse is GenericFailureResponse failedResponse) { this.logger.LogInformation($"Write Card failed with module code: {failedResponse.模块返回状态}, cpu code: {failedResponse.CpuCardState} for module with physical address: {failedResponse.ModulePhysicalAddress}"); return new CardOperationResult(400) { CardReaderModule = this, ModuleResultCode = failedResponse.模块返回状态.ToString(), CpuCardResultCode = failedResponse.CpuCardState.ToString() }; } else { this.logger.LogInformation($"Write Card timed out for module (phyAdrs: {modulePhysicalAddress}, comm: {this.context.Communicator.Identity})"); return new CardOperationResult(500) { CardReaderModule = this, ModuleResultCode = "Device response timed out" }; } } internal async Task ReadCard(byte modulePhysicalAddress, byte fileId, byte blockAddress, byte[] readKeyBytes) { if (readKeyBytes == null || !readKeyBytes.Any()) throw new ArgumentException(nameof(readKeyBytes)); var readAppFileRequestResponse = await this.context.Outgoing.WriteAsync( new ReadAppFileRequest(fileId, blockAddress, readKeyBytes) { ModulePhysicalAddress = modulePhysicalAddress }, (_, testResponse) => testResponse.ModulePhysicalAddress == modulePhysicalAddress && (testResponse is GenericFailureResponse || testResponse is ReadAppFileResponse), 4000); if (readAppFileRequestResponse is ReadAppFileResponse successResponse) { return new CardOperationResult(200, successResponse.Data) { CardReaderModule = this }; } else if (readAppFileRequestResponse is GenericFailureResponse failedResponse) { this.logger.LogInformation($"Read Card failed with module code: {failedResponse.模块返回状态}, cpu code: {failedResponse.CpuCardState} for module with physical address: {failedResponse.ModulePhysicalAddress}"); if (failedResponse.模块返回状态 == MessageEntity.IncomingMessageBase.ModuleStateEnum.读取CPU卡文件失败) return new CardOperationResult(400) { CardReaderModule = this, ModuleResultCode = failedResponse.模块返回状态.ToString() + ", 请保持卡片靠近读卡器", CpuCardResultCode = failedResponse.CpuCardState.ToString() }; else return new CardOperationResult(400) { CardReaderModule = this, ModuleResultCode = failedResponse.模块返回状态.ToString(), CpuCardResultCode = failedResponse.CpuCardState.ToString() }; } else { this.logger.LogInformation($"Read Card timed out for module (phyAdrs: {modulePhysicalAddress}, comm: {this.context.Communicator.Identity})"); return new CardOperationResult(500) { CardReaderModule = this, ModuleResultCode = "与读卡器通讯超时" }; } } internal async Task ModifyPredefinedFileKey(byte modulePhysicalAddress, PredefinedFileKeyTypeEnum fileKeyType, byte[] oldKeyBytes, byte[] newKeyBytes) { if (oldKeyBytes == null || !oldKeyBytes.Any()) throw new ArgumentException(nameof(oldKeyBytes)); if (newKeyBytes == null || !newKeyBytes.Any()) throw new ArgumentException(nameof(newKeyBytes)); var modifyPredefinedFileKeyResponse = await this.context.Outgoing.WriteAsync( new ModifyPredefinedFileKey(fileKeyType, oldKeyBytes, newKeyBytes) { ModulePhysicalAddress = modulePhysicalAddress }, (_, testResponse) => testResponse.ModulePhysicalAddress == modulePhysicalAddress && (testResponse is GenericFailureResponse || testResponse is GenericSuccessResponse), 4000); if (modifyPredefinedFileKeyResponse is GenericSuccessResponse successResponse) { return new CardOperationResult(200) { CardReaderModule = this }; } else if (modifyPredefinedFileKeyResponse is GenericFailureResponse failedResponse) { this.logger.LogInformation($"Modify Predefined FileKey failed with module code: {failedResponse.模块返回状态}, cpu code: {failedResponse.CpuCardState} for module with physical address: {failedResponse.ModulePhysicalAddress}"); return new CardOperationResult(400) { CardReaderModule = this, ModuleResultCode = failedResponse.模块返回状态.ToString(), CpuCardResultCode = failedResponse.CpuCardState.ToString() }; } else { this.logger.LogInformation($"Modify Predefined FileKey timed out for module (phyAdrs: {modulePhysicalAddress}, comm: {this.context.Communicator.Identity})"); return new CardOperationResult(500) { CardReaderModule = this, ModuleResultCode = "Device response timed out" }; } } } public class CardOperationResult { public CardOperationResult(int overallResultCode, byte[] data) { this.OverallResultCode = overallResultCode; if (data != null && data.Any()) { this.Data = data.Select(b => (int)b).ToArray(); this.PrettyUTF8DecodedData = Encoding.UTF8.GetString(data); this.PrettyHexStrData = "0x" + data.ToHexLogString(); } } public CardOperationResult(int overallResultCode) : this(overallResultCode, null) { } public int OverallResultCode { get; set; } public string ModuleResultCode { get; set; } public string CpuCardResultCode { get; set; } public CardReaderModule CardReaderModule { get; set; } /// /// for better serialize, here use int instead of byte. /// public int[] Data { get; } public string PrettyUTF8DecodedData { get; } /// /// like: 0x11 22 33 44 /// public string PrettyHexStrData { get; } } #region device config public class CardReaderModuleConfigV1 { public byte PhysicalAddress { get; set; } /// /// a meaningful name is required, and must be global unique /// public string Name { get; set; } public string Description { get; set; } } public class DeviceConfigV1 { public List CardReaderModuleConfigs { get; set; } } #endregion public GroupHandler(DeviceConfigV1 deviceConfig, IServiceProvider services) { this.services = services; var loggerFactory = services.GetRequiredService(); this.logger = loggerFactory.CreateLogger("DynamicPrivate_ShengJu_CUT100_DES"); if (deviceConfig.CardReaderModuleConfigs.Select(mc => mc.PhysicalAddress).GroupBy(pa => pa).Any(g => g.Count() >= 2)) throw new ArgumentException("Duplicated PhysicalAddress for moduels were found, please make sure they're unique per comm channel"); if (deviceConfig.CardReaderModuleConfigs.Select(mc => mc.Name).GroupBy(pa => pa).Any(g => g.Count() >= 2)) throw new ArgumentException("Duplicated card module name were found, card module name is essential for tell the usage of it, please make sure they're unique"); this.deviceConfig = deviceConfig; this.cardReaderModules = deviceConfig.CardReaderModuleConfigs.Select(mc => new CardReaderModule(mc.PhysicalAddress, this.logger) { Name = mc.Name, Description = mc.Description, ModuleState = CardReaderModule.CardReaderModuleStateEnum.Offline, }).ToList(); } public override void Init(IContext context) { base.Init(context); this.context = context; this.context.Communicator.OnConnected += (sender, e) => { }; this.context.Communicator.OnDisconnected += (sender, e) => { }; foreach (var m in this.cardReaderModules) { m.SetContext(context); m.UnderlyingCommunicatorIdentity = this.context.Communicator.Identity; }; //accuracy is 1000ms this.singleModuleOfflineCheckTimer = new Timer(1000); this.singleModuleOfflineCheckTimer.Elapsed += async (a, b) => { var offlineModules = this.cardReaderModules.Where(b => DateTime.Now.Subtract(b.LastIncomingMessageReceivedTime ?? DateTime.MinValue).TotalMilliseconds >= singleModuleOfflineTimeThresholdByMs); foreach (var om in offlineModules) { if (om.ModuleState == CardReaderModule.CardReaderModuleStateEnum.Offline) continue; om.ModuleState = CardReaderModule.CardReaderModuleStateEnum.Offline; this.OnModuleStateChange?.Invoke(this, new ModuleStateChangeEventArg(om)); var universalApiHub = this.services.GetRequiredService(); await universalApiHub.FireEvent(this.context.Processor, GenericAlarm.UniversalApiEventName, new GenericAlarm[] { new GenericAlarm() { Category = $"单块读卡器模块通讯断开", Title = $"单块读卡器模块通讯断开", Detail = $"单块读卡器模块通讯断开, Module: {om.Name??""} with address: {om.PhysicalAddress}", Severity = GenericAlarmSeverity.Warning } }); } }; this.singleModuleOfflineCheckTimer.Start(); var activePollingOutgoing = this.context.Outgoing as TimeWindowWithActivePollingOutgoing; int prePolledIndex = 0; activePollingOutgoing.PollingMsgProducer = () => { var poll = new ActivateATypeCardRequest(); poll.ModulePhysicalAddress = this.deviceConfig.CardReaderModuleConfigs[prePolledIndex].PhysicalAddress; prePolledIndex++; if (prePolledIndex == this.deviceConfig.CardReaderModuleConfigs.Count) prePolledIndex = 0; return poll; }; this.context.Incoming.LongTimeNoSeeMessageTimeout = 12000; this.context.Incoming.OnLongTimeNoSeeMessage += async (a, b) => { this.logger.LogInformation("ShengJu_CUT100_DES group is offline due to long time no see msg incoming"); foreach (var m in this.CardReaderModuels) m.ModuleState = CardReaderModule.CardReaderModuleStateEnum.Offline; this.OnModuleStateChange?.Invoke(this, new ModuleStateChangeEventArg(this.cardReaderModules)); var universalApiHub = this.services.GetRequiredService(); await universalApiHub?.FireEvent(this.context.Processor, GenericAlarm.UniversalApiEventName, new[] { new GenericAlarm() { Title = "同一通讯组中的多个(如有)读卡器模块均离线", Detail=this.deviceConfig.CardReaderModuleConfigs.Select(mc=>$"Module: {mc.Name??""} with address: {mc.PhysicalAddress}").Aggregate("",(acc,n)=>acc+", "+n)+" are offline." } }); }; #region Load keys //this.blankCardDefaultRootKey = Enumerable.Repeat(0x00, 16).ToArray(); //this.newRootKey = Enumerable.Repeat(0x11, 16).ToArray(); //this.file1DefaultReadKey = Enumerable.Repeat(0x00, 16).ToArray(); //this.file1DefaultWriteKey = Enumerable.Repeat(0x00, 16).ToArray(); #endregion } public override async Task Process(IContext context) { if (!(this.cardReaderModules.FirstOrDefault(b => b.PhysicalAddress == context.Incoming.Message.ModulePhysicalAddress) is CardReaderModule operatingModule)) { this.logger.LogInformation($"Could not find local defined module with physical address: {context.Incoming.Message.ModulePhysicalAddress}, please define it in device config, now will ignore this incoming device message"); return; } operatingModule.LastIncomingMessageReceivedTime = DateTime.Now; if (operatingModule.ModuleState == CardReaderModule.CardReaderModuleStateEnum.Offline) { operatingModule.ModuleState = CardReaderModule.CardReaderModuleStateEnum.Online; var readModuleInfoResponse = await this.context.Outgoing.WriteAsync( new ReadModuleInfoRequest() { ModulePhysicalAddress = operatingModule.PhysicalAddress }, (_, testResponse) => testResponse.ModulePhysicalAddress == operatingModule.PhysicalAddress && (testResponse is GenericFailureResponse || testResponse is ReadModuleInfoResponse), 4000); if (readModuleInfoResponse is ReadModuleInfoResponse successReadModuleInfoResponse) { operatingModule.HardwareFirmware = successReadModuleInfoResponse.ModelAndVersion; this.logger.LogInformation($"Module (phyAdrs: {operatingModule.PhysicalAddress}, comm: {operatingModule.UnderlyingCommunicatorIdentity}) is online, read module Info: {successReadModuleInfoResponse.ModelAndVersion}"); } else if (readModuleInfoResponse is GenericFailureResponse failedReadModuleInfoResponse) { this.logger.LogInformation($"Module (phyAdrs: {operatingModule.PhysicalAddress}, comm: {operatingModule.UnderlyingCommunicatorIdentity}) is online, but read module info failed due to response: {failedReadModuleInfoResponse.模块返回状态}"); } else { this.logger.LogInformation($"Module (phyAdrs: {operatingModule.PhysicalAddress}, comm: {operatingModule.UnderlyingCommunicatorIdentity}) is online, but read module info timed out"); } this.OnModuleStateChange?.Invoke(this, new ModuleStateChangeEventArg(operatingModule)); var universalApiHub = this.services.GetRequiredService(); await universalApiHub.FireEvent(this.context.Processor, GenericAlarm.UniversalApiEventName, new GenericAlarm[] { new GenericAlarm() { Category = $"单块读卡器模块上线", Title = $"单块读卡器模块上线", Detail = $"单块读卡器模块上线, Module: {operatingModule.Name??""} with address: {operatingModule.PhysicalAddress} on comm: {operatingModule.UnderlyingCommunicatorIdentity}", Severity = GenericAlarmSeverity.Warning } }); } if (context.Incoming.Message is ActivateATypeCardResponse activateATypeCardResponse && activateATypeCardResponse.UID.Any()) { this.logger.LogDebug($"Card with UID: 0x{activateATypeCardResponse.UID.ToHexLogString()} is read on module (phyAdrs: {operatingModule.PhysicalAddress}, comm: {operatingModule.UnderlyingCommunicatorIdentity})");//, will auto read card app dirs..."); this.OnCardTap?.Invoke(this, new CardTapEventArg(activateATypeCardResponse.UID, operatingModule)); var universalApiHub = this.services.GetRequiredService(); await universalApiHub?.FireEvent(this.context.Processor, OnCardTapEventName, new { Module = this.cardReaderModules.FirstOrDefault(b => b.PhysicalAddress == context.Incoming.Message.ModulePhysicalAddress), activateATypeCardResponse.UID }); //var readAppDirsResponse = await this.context.Outgoing.WriteAsync( // new ReadAppDirsRequest(this.newRootKey) { ModulePhysicalAddress = md.PhysicalAddress }, // (_, testResponse) => testResponse.ModulePhysicalAddress == md.PhysicalAddress && (testResponse is GenericFailureResponse || testResponse is ReadAppDirsResponse), 4000); //if (readAppDirsResponse is ReadAppDirsResponse successReadAppDirsResponse) //{ // this.logger.LogInformation($" Read Card AppDirs, count: {successReadAppDirsResponse.DirCount}, " + // $"dirIds: {successReadAppDirsResponse.DirIds.Select(did => did.Select(i => i.ToString("X2")).Aggregate("0x", (acc, n) => acc + " " + n)).Aggregate((acc, n) => acc + ", " + n)} " + // $"for module with physical address: {successReadAppDirsResponse.ModulePhysicalAddress}"); //} //else if (readAppDirsResponse is GenericFailureResponse failedReadAppDirsResponse) //{ // this.logger.LogInformation($" Read Card AppDirs failed with module code: {failedReadAppDirsResponse.模块返回状态}, cpu code: {failedReadAppDirsResponse.CpuCardState} for module with physical address: {failedReadAppDirsResponse.ModulePhysicalAddress}"); //} //else //{ // this.logger.LogInformation($" Read Card AppDirs timed out for module with physical address: {md.PhysicalAddress}"); //} } } public async Task FormatCardByResetToNewRootKeyAndBuildDefaultFileStructures(string cardReaderModuleName, byte[] oldRootKeyBytes, byte[] newRootKeyBytes) { IEnumerable targetModules; if (cardReaderModuleName != "*") targetModules = this.cardReaderModules.Where(m => m.Name == cardReaderModuleName); else targetModules = this.cardReaderModules; if (!targetModules.Any()) return new CardOperationResult[] { new CardOperationResult(404) { ModuleResultCode = $"找不到读卡器, 它的名称是: {cardReaderModuleName ?? ""}" } }; var batchFormatTasks = targetModules.Select(m => m.FormatCardByResetToNewRootKeyAndBuildDefaultFileStructures(m.PhysicalAddress, oldRootKeyBytes.Take(16).ToArray(), newRootKeyBytes.Take(16).ToArray())); var results = await Task.WhenAll(batchFormatTasks); return results; } public async Task WriteCard(string cardReaderModuleName, byte fileId, byte blockAddress, byte[] dataBytes, byte[] writeKeyBytes) { IEnumerable targetModules; if (cardReaderModuleName != "*") targetModules = this.cardReaderModules.Where(m => m.Name == cardReaderModuleName); else targetModules = this.cardReaderModules; if (!targetModules.Any()) return new CardOperationResult[] { new CardOperationResult(404) { ModuleResultCode = $"找不到读卡器, 它的名称是: {cardReaderModuleName ?? ""}" } }; var batchWriteTasks = targetModules.Select(m => m.WriteCard(m.PhysicalAddress, fileId, blockAddress, dataBytes, writeKeyBytes.Take(16).ToArray())); var results = await Task.WhenAll(batchWriteTasks); return results; } public async Task ReadCard(string cardReaderModuleName, byte fileId, byte blockAddress, byte[] readKeyBytes) { IEnumerable targetModules; if (cardReaderModuleName != "*") targetModules = this.cardReaderModules.Where(m => m.Name == cardReaderModuleName); else targetModules = this.cardReaderModules; if (!targetModules.Any()) return new CardOperationResult[] { new CardOperationResult(404) { ModuleResultCode = $"找不到读卡器, 它的名称是: {cardReaderModuleName ?? ""}" } }; var batchReadTasks = targetModules.Select(m => m.ReadCard(m.PhysicalAddress, fileId, blockAddress, readKeyBytes.Take(16).ToArray())); var results = await Task.WhenAll(batchReadTasks); return results; } public async Task ReadCardUID(string cardReaderModuleName) { IEnumerable targetModules; if (cardReaderModuleName != "*") targetModules = this.cardReaderModules.Where(m => m.Name == cardReaderModuleName); else targetModules = this.cardReaderModules; if (!targetModules.Any()) return new CardOperationResult[] { new CardOperationResult(404) { ModuleResultCode = $"找不到读卡器, 它的名称是: {cardReaderModuleName ?? ""}" } }; var batchTasks = targetModules.Select(m => m.ReadCardUID(m.PhysicalAddress)); var results = await Task.WhenAll(batchTasks); return results; } public async Task ModifyPredefinedFileKey(string cardReaderModuleName, PredefinedFileKeyTypeEnum fileKeyType, byte[] oldKeyBytes, byte[] newKeyBytes) { IEnumerable targetModules; if (cardReaderModuleName != "*") targetModules = this.cardReaderModules.Where(m => m.Name == cardReaderModuleName); else targetModules = this.cardReaderModules; if (!targetModules.Any()) return new CardOperationResult[] { new CardOperationResult(404) { ModuleResultCode = $"找不到读卡器, 它的名称是: {cardReaderModuleName ?? ""}" } }; var batchTasks = targetModules.Select(m => m.ModifyPredefinedFileKey(m.PhysicalAddress, fileKeyType, oldKeyBytes.Take(16).ToArray(), newKeyBytes.Take(16).ToArray())); var results = await Task.WhenAll(batchTasks); return results; } } }