GroupHandler.cs 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516
  1. using Edge.Core.Parser;
  2. using Edge.Core.Parser.BinaryParser.Util;
  3. using Edge.Core.Processor;
  4. using Edge.Core.Processor.Communicator;
  5. using Edge.Core.Processor.Dispatcher.Attributes;
  6. using Edge.Core.UniversalApi;
  7. using Microsoft.Extensions.DependencyInjection;
  8. using Microsoft.Extensions.Logging;
  9. using Microsoft.Extensions.Logging.Abstractions;
  10. using ShengJu_CUT100_DES.MessageEntity.Incoming;
  11. using ShengJu_CUT100_DES.MessageEntity.Outgoing;
  12. using System;
  13. using System.Collections.Generic;
  14. using System.IO;
  15. using System.Linq;
  16. using System.Text;
  17. using System.Threading.Tasks;
  18. using System.Timers;
  19. using static ShengJu_CUT100_DES.MessageEntity.Outgoing.ModifyPredefinedFileKey;
  20. namespace ShengJu_CUT100_DES
  21. {
  22. /// <summary>
  23. /// at least at 2021.4, 这模块还不支持并线,因为模块内部并没有区分请求中的地址值。
  24. /// </summary>
  25. [MetaPartsRequired(typeof(HalfDuplexActivePollingDeviceProcessor<,>))]
  26. [MetaPartsRequired(typeof(ComPortCommunicator<>))]
  27. [MetaPartsRequired(typeof(TcpClientCommunicator<>))]
  28. [MetaPartsDescriptor(
  29. "lang-zh-cn:ShengJu CUT100_DES 感应卡读写卡模块lang-en-us:ShengJu CUT100_DES contactless card read&write module",
  30. "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",
  31. new[] { "lang-zh-cn:支付终端lang-en-us:Terminal" })]
  32. [UniversalApi(Name = GenericAlarm.UniversalApiEventName, EventDataType = typeof(GenericAlarm[]), Description = "Fire GenericAlarms to AlarmBar for attracting users.")]
  33. [UniversalApi(Name = OnCardTapEventName, EventDataType = typeof(object), Description = "Fired once a DesFire EV1 card detected by module and card UID is read")]
  34. public class GroupHandler : TestableActivePollingDeviceHandler<byte[], ShengJu_CUT100_DES.MessageEntity.MessageBase>
  35. {
  36. public const string OnCardTapEventName = "OnCardTap";
  37. public event EventHandler<ModuleStateChangeEventArg> OnModuleStateChange;
  38. public event EventHandler<CardTapEventArg> OnCardTap;
  39. private ILogger logger = NullLogger.Instance;
  40. private IServiceProvider services;
  41. protected IContext<byte[], MessageEntity.MessageBase> context;
  42. private DeviceConfigV1 deviceConfig;
  43. private IEnumerable<CardReaderModule> cardReaderModules;
  44. private const int singleModuleOfflineTimeThresholdByMs = 15000;
  45. //private Dictionary<byte, DateTime> boardsLastImcomingMsgReceivedTimes;
  46. private Timer singleModuleOfflineCheckTimer;
  47. [UniversalApi]
  48. public IEnumerable<CardReaderModule> CardReaderModuels => this.cardReaderModules;
  49. public class CardReaderModule
  50. {
  51. private ILogger logger = NullLogger.Instance;
  52. private IContext<byte[], MessageEntity.MessageBase> context;
  53. public enum CardReaderModuleStateEnum { Offline, Online }
  54. public string Name { get; set; }
  55. public string Description { get; set; }
  56. public byte PhysicalAddress { get; }
  57. public string HardwareFirmware { get; set; }
  58. public string UnderlyingCommunicatorIdentity { get; set; }
  59. public CardReaderModuleStateEnum ModuleState { get; set; }
  60. /// <summary>
  61. /// used for track the last received message which for caculate the offline state of this device.
  62. /// </summary>
  63. public DateTime? LastIncomingMessageReceivedTime { get; set; }
  64. internal CardReaderModule(byte physicalAddress, ILogger logger)
  65. {
  66. this.PhysicalAddress = physicalAddress;
  67. this.logger = logger;
  68. }
  69. internal void SetContext(IContext<byte[], MessageEntity.MessageBase> context)
  70. {
  71. this.context = context;
  72. }
  73. internal async Task<CardOperationResult> ReadCardUID(byte modulePhysicalAddress)
  74. {
  75. var readCardUidResponse = await this.context.Outgoing.WriteAsync(
  76. new ActivateATypeCardRequest() { ModulePhysicalAddress = modulePhysicalAddress },
  77. (_, testResponse) => testResponse.ModulePhysicalAddress == modulePhysicalAddress
  78. && (testResponse is GenericFailureResponse || testResponse is ActivateATypeCardResponse), 4000);
  79. if (readCardUidResponse is ActivateATypeCardResponse rp)
  80. {
  81. if (rp.UID.Any())
  82. return new CardOperationResult(200, rp.UID) { CardReaderModule = this };
  83. else
  84. return new CardOperationResult(404) { CardReaderModule = this, ModuleResultCode = "No UID was read" };
  85. }
  86. else if (readCardUidResponse is GenericFailureResponse failedResponse)
  87. {
  88. this.logger.LogInformation($"ReadCardUID failed with module code: {failedResponse.模块返回状态}, cpu code: {failedResponse.CpuCardState} for module with physical address: {failedResponse.ModulePhysicalAddress}");
  89. return new CardOperationResult(400) { CardReaderModule = this, ModuleResultCode = failedResponse.模块返回状态.ToString(), CpuCardResultCode = failedResponse.CpuCardState.ToString() };
  90. }
  91. else
  92. {
  93. this.logger.LogInformation($"ReadCardUID timed out for module (phyAdrs: {modulePhysicalAddress}, comm: {this.context.Communicator.Identity})");
  94. return new CardOperationResult(500) { CardReaderModule = this, ModuleResultCode = "Device response timed out" };
  95. }
  96. }
  97. internal async Task<CardOperationResult> FormatCardByResetToNewRootKeyAndBuildDefaultFileStructures(byte modulePhysicalAddress, byte[] oldRootKeyBytes, byte[] newRootKeyBytes)
  98. {
  99. if (oldRootKeyBytes == null || !oldRootKeyBytes.Any())
  100. throw new ArgumentException(nameof(oldRootKeyBytes));
  101. if (newRootKeyBytes == null || !newRootKeyBytes.Any())
  102. throw new ArgumentException(nameof(newRootKeyBytes));
  103. var formatCardResponse = await this.context.Outgoing.WriteAsync(
  104. new FormatCardRequest(oldRootKeyBytes, newRootKeyBytes) { ModulePhysicalAddress = modulePhysicalAddress },
  105. (_, testResponse) => testResponse.ModulePhysicalAddress == modulePhysicalAddress
  106. && (testResponse is GenericFailureResponse || testResponse is GenericSuccessResponse), 4000);
  107. if (formatCardResponse is GenericSuccessResponse)
  108. {
  109. this.logger.LogInformation($"Format Card successfully on module (phyAdrs: {modulePhysicalAddress}, comm: {this.context.Communicator.Identity}), will auto re-read card app dirs...");
  110. var readAppDirsResponse = await this.context.Outgoing.WriteAsync(
  111. new ReadAppDirsRequest(newRootKeyBytes) { ModulePhysicalAddress = modulePhysicalAddress },
  112. (_, testResponse) => testResponse.ModulePhysicalAddress == modulePhysicalAddress && (testResponse is GenericFailureResponse || testResponse is ReadAppDirsResponse), 4000);
  113. if (readAppDirsResponse is ReadAppDirsResponse successReadAppDirsResponse)
  114. {
  115. this.logger.LogInformation($" Read Card AppDirs, count: {successReadAppDirsResponse.DirCount}, " +
  116. $"dirIds: {successReadAppDirsResponse.DirIds.Select(did => did.Select(i => i.ToString("X2")).Aggregate("0x", (acc, n) => acc + " " + n)).Aggregate((acc, n) => acc + ", " + n)} " +
  117. $"on module (phyAdrs: {successReadAppDirsResponse.ModulePhysicalAddress}, comm: {this.context.Communicator.Identity})");
  118. }
  119. else if (readAppDirsResponse is GenericFailureResponse failedReadAppDirsResponse)
  120. {
  121. 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})");
  122. }
  123. else
  124. {
  125. this.logger.LogInformation($" Read Card AppDirs timed out for module (phyAdrs: {modulePhysicalAddress}, comm: {this.context.Communicator.Identity})");
  126. }
  127. return new CardOperationResult(200) { CardReaderModule = this };
  128. }
  129. else if (formatCardResponse is GenericFailureResponse failedFormatCardResponse)
  130. {
  131. this.logger.LogInformation($"Format Card failed with module code: {failedFormatCardResponse.模块返回状态}, cpu code: {failedFormatCardResponse.CpuCardState} for module with physical address: {failedFormatCardResponse.ModulePhysicalAddress}");
  132. return new CardOperationResult(400) { CardReaderModule = this, ModuleResultCode = failedFormatCardResponse.模块返回状态.ToString(), CpuCardResultCode = failedFormatCardResponse.CpuCardState.ToString() };
  133. }
  134. else
  135. {
  136. this.logger.LogInformation($"Format Card timed out for module with physical address: {modulePhysicalAddress}");
  137. return new CardOperationResult(500) { CardReaderModule = this, ModuleResultCode = "Device response timed out" };
  138. }
  139. }
  140. internal async Task<CardOperationResult> WriteCard(byte modulePhysicalAddress, byte fileId, byte blockAddress, byte[] dataBytes, byte[] writeKeyBytes)
  141. {
  142. if (writeKeyBytes == null || !writeKeyBytes.Any())
  143. throw new ArgumentException(nameof(writeKeyBytes));
  144. var writeAppFileRequestResponse = await this.context.Outgoing.WriteAsync(
  145. new WriteAppFileRequest(fileId, blockAddress, writeKeyBytes, dataBytes) { ModulePhysicalAddress = modulePhysicalAddress },
  146. (_, testResponse) => testResponse.ModulePhysicalAddress == modulePhysicalAddress
  147. && (testResponse is GenericFailureResponse || testResponse is GenericSuccessResponse), 4000);
  148. if (writeAppFileRequestResponse is GenericSuccessResponse successResponse)
  149. {
  150. return new CardOperationResult(200) { CardReaderModule = this };
  151. }
  152. else if (writeAppFileRequestResponse is GenericFailureResponse failedResponse)
  153. {
  154. this.logger.LogInformation($"Write Card failed with module code: {failedResponse.模块返回状态}, cpu code: {failedResponse.CpuCardState} for module with physical address: {failedResponse.ModulePhysicalAddress}");
  155. return new CardOperationResult(400) { CardReaderModule = this, ModuleResultCode = failedResponse.模块返回状态.ToString(), CpuCardResultCode = failedResponse.CpuCardState.ToString() };
  156. }
  157. else
  158. {
  159. this.logger.LogInformation($"Write Card timed out for module (phyAdrs: {modulePhysicalAddress}, comm: {this.context.Communicator.Identity})");
  160. return new CardOperationResult(500) { CardReaderModule = this, ModuleResultCode = "Device response timed out" };
  161. }
  162. }
  163. internal async Task<CardOperationResult> ReadCard(byte modulePhysicalAddress, byte fileId, byte blockAddress, byte[] readKeyBytes)
  164. {
  165. if (readKeyBytes == null || !readKeyBytes.Any())
  166. throw new ArgumentException(nameof(readKeyBytes));
  167. var readAppFileRequestResponse = await this.context.Outgoing.WriteAsync(
  168. new ReadAppFileRequest(fileId, blockAddress, readKeyBytes) { ModulePhysicalAddress = modulePhysicalAddress },
  169. (_, testResponse) => testResponse.ModulePhysicalAddress == modulePhysicalAddress
  170. && (testResponse is GenericFailureResponse || testResponse is ReadAppFileResponse), 4000);
  171. if (readAppFileRequestResponse is ReadAppFileResponse successResponse)
  172. {
  173. return new CardOperationResult(200, successResponse.Data) { CardReaderModule = this };
  174. }
  175. else if (readAppFileRequestResponse is GenericFailureResponse failedResponse)
  176. {
  177. this.logger.LogInformation($"Read Card failed with module code: {failedResponse.模块返回状态}, cpu code: {failedResponse.CpuCardState} for module with physical address: {failedResponse.ModulePhysicalAddress}");
  178. if (failedResponse.模块返回状态 == MessageEntity.IncomingMessageBase.ModuleStateEnum.读取CPU卡文件失败)
  179. return new CardOperationResult(400) { CardReaderModule = this, ModuleResultCode = failedResponse.模块返回状态.ToString() + ", 请保持卡片靠近读卡器", CpuCardResultCode = failedResponse.CpuCardState.ToString() };
  180. else
  181. return new CardOperationResult(400) { CardReaderModule = this, ModuleResultCode = failedResponse.模块返回状态.ToString(), CpuCardResultCode = failedResponse.CpuCardState.ToString() };
  182. }
  183. else
  184. {
  185. this.logger.LogInformation($"Read Card timed out for module (phyAdrs: {modulePhysicalAddress}, comm: {this.context.Communicator.Identity})");
  186. return new CardOperationResult(500) { CardReaderModule = this, ModuleResultCode = "与读卡器通讯超时" };
  187. }
  188. }
  189. internal async Task<CardOperationResult> ModifyPredefinedFileKey(byte modulePhysicalAddress, PredefinedFileKeyTypeEnum fileKeyType, byte[] oldKeyBytes, byte[] newKeyBytes)
  190. {
  191. if (oldKeyBytes == null || !oldKeyBytes.Any())
  192. throw new ArgumentException(nameof(oldKeyBytes));
  193. if (newKeyBytes == null || !newKeyBytes.Any())
  194. throw new ArgumentException(nameof(newKeyBytes));
  195. var modifyPredefinedFileKeyResponse = await this.context.Outgoing.WriteAsync(
  196. new ModifyPredefinedFileKey(fileKeyType, oldKeyBytes, newKeyBytes) { ModulePhysicalAddress = modulePhysicalAddress },
  197. (_, testResponse) => testResponse.ModulePhysicalAddress == modulePhysicalAddress
  198. && (testResponse is GenericFailureResponse || testResponse is GenericSuccessResponse), 4000);
  199. if (modifyPredefinedFileKeyResponse is GenericSuccessResponse successResponse)
  200. {
  201. return new CardOperationResult(200) { CardReaderModule = this };
  202. }
  203. else if (modifyPredefinedFileKeyResponse is GenericFailureResponse failedResponse)
  204. {
  205. this.logger.LogInformation($"Modify Predefined FileKey failed with module code: {failedResponse.模块返回状态}, cpu code: {failedResponse.CpuCardState} for module with physical address: {failedResponse.ModulePhysicalAddress}");
  206. return new CardOperationResult(400) { CardReaderModule = this, ModuleResultCode = failedResponse.模块返回状态.ToString(), CpuCardResultCode = failedResponse.CpuCardState.ToString() };
  207. }
  208. else
  209. {
  210. this.logger.LogInformation($"Modify Predefined FileKey timed out for module (phyAdrs: {modulePhysicalAddress}, comm: {this.context.Communicator.Identity})");
  211. return new CardOperationResult(500) { CardReaderModule = this, ModuleResultCode = "Device response timed out" };
  212. }
  213. }
  214. }
  215. public class CardOperationResult
  216. {
  217. public CardOperationResult(int overallResultCode, byte[] data)
  218. {
  219. this.OverallResultCode = overallResultCode;
  220. if (data != null && data.Any())
  221. {
  222. this.Data = data.Select(b => (int)b).ToArray();
  223. this.PrettyUTF8DecodedData = Encoding.UTF8.GetString(data);
  224. this.PrettyHexStrData = "0x" + data.ToHexLogString();
  225. }
  226. }
  227. public CardOperationResult(int overallResultCode) : this(overallResultCode, null)
  228. {
  229. }
  230. public int OverallResultCode { get; set; }
  231. public string ModuleResultCode { get; set; }
  232. public string CpuCardResultCode { get; set; }
  233. public CardReaderModule CardReaderModule { get; set; }
  234. /// <summary>
  235. /// for better serialize, here use int instead of byte.
  236. /// </summary>
  237. public int[] Data { get; }
  238. public string PrettyUTF8DecodedData { get; }
  239. /// <summary>
  240. /// like: 0x11 22 33 44
  241. /// </summary>
  242. public string PrettyHexStrData { get; }
  243. }
  244. #region device config
  245. public class CardReaderModuleConfigV1
  246. {
  247. public byte PhysicalAddress { get; set; }
  248. /// <summary>
  249. /// a meaningful name is required, and must be global unique
  250. /// </summary>
  251. public string Name { get; set; }
  252. public string Description { get; set; }
  253. }
  254. public class DeviceConfigV1
  255. {
  256. public List<CardReaderModuleConfigV1> CardReaderModuleConfigs { get; set; }
  257. }
  258. #endregion
  259. public GroupHandler(DeviceConfigV1 deviceConfig, IServiceProvider services)
  260. {
  261. this.services = services;
  262. var loggerFactory = services.GetRequiredService<ILoggerFactory>();
  263. this.logger = loggerFactory.CreateLogger("DynamicPrivate_ShengJu_CUT100_DES");
  264. if (deviceConfig.CardReaderModuleConfigs.Select(mc => mc.PhysicalAddress).GroupBy(pa => pa).Any(g => g.Count() >= 2))
  265. throw new ArgumentException("Duplicated PhysicalAddress for moduels were found, please make sure they're unique per comm channel");
  266. if (deviceConfig.CardReaderModuleConfigs.Select(mc => mc.Name).GroupBy(pa => pa).Any(g => g.Count() >= 2))
  267. 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");
  268. this.deviceConfig = deviceConfig;
  269. this.cardReaderModules = deviceConfig.CardReaderModuleConfigs.Select(mc => new CardReaderModule(mc.PhysicalAddress, this.logger)
  270. {
  271. Name = mc.Name,
  272. Description = mc.Description,
  273. ModuleState = CardReaderModule.CardReaderModuleStateEnum.Offline,
  274. }).ToList();
  275. }
  276. public override void Init(IContext<byte[], MessageEntity.MessageBase> context)
  277. {
  278. base.Init(context);
  279. this.context = context;
  280. this.context.Communicator.OnConnected += (sender, e) => { };
  281. this.context.Communicator.OnDisconnected += (sender, e) => { };
  282. foreach (var m in this.cardReaderModules)
  283. {
  284. m.SetContext(context);
  285. m.UnderlyingCommunicatorIdentity = this.context.Communicator.Identity;
  286. };
  287. //accuracy is 1000ms
  288. this.singleModuleOfflineCheckTimer = new Timer(1000);
  289. this.singleModuleOfflineCheckTimer.Elapsed += async (a, b) =>
  290. {
  291. var offlineModules = this.cardReaderModules.Where(b => DateTime.Now.Subtract(b.LastIncomingMessageReceivedTime ?? DateTime.MinValue).TotalMilliseconds >= singleModuleOfflineTimeThresholdByMs);
  292. foreach (var om in offlineModules)
  293. {
  294. if (om.ModuleState == CardReaderModule.CardReaderModuleStateEnum.Offline) continue;
  295. om.ModuleState = CardReaderModule.CardReaderModuleStateEnum.Offline;
  296. this.OnModuleStateChange?.Invoke(this, new ModuleStateChangeEventArg(om));
  297. var universalApiHub = this.services.GetRequiredService<UniversalApiHub>();
  298. await universalApiHub.FireEvent(this.context.Processor, GenericAlarm.UniversalApiEventName,
  299. new GenericAlarm[] {
  300. new GenericAlarm()
  301. {
  302. Category = $"单块读卡器模块通讯断开",
  303. Title = $"单块读卡器模块通讯断开",
  304. Detail = $"单块读卡器模块通讯断开, Module: {om.Name??""} with address: {om.PhysicalAddress}",
  305. Severity = GenericAlarmSeverity.Warning
  306. } });
  307. }
  308. };
  309. this.singleModuleOfflineCheckTimer.Start();
  310. var activePollingOutgoing =
  311. this.context.Outgoing as TimeWindowWithActivePollingOutgoing<byte[], MessageEntity.MessageBase>;
  312. int prePolledIndex = 0;
  313. activePollingOutgoing.PollingMsgProducer = () =>
  314. {
  315. var poll = new ActivateATypeCardRequest();
  316. poll.ModulePhysicalAddress = this.deviceConfig.CardReaderModuleConfigs[prePolledIndex].PhysicalAddress;
  317. prePolledIndex++;
  318. if (prePolledIndex == this.deviceConfig.CardReaderModuleConfigs.Count)
  319. prePolledIndex = 0;
  320. return poll;
  321. };
  322. this.context.Incoming.LongTimeNoSeeMessageTimeout = 12000;
  323. this.context.Incoming.OnLongTimeNoSeeMessage += async (a, b) =>
  324. {
  325. this.logger.LogInformation("ShengJu_CUT100_DES group is offline due to long time no see msg incoming");
  326. foreach (var m in this.CardReaderModuels)
  327. m.ModuleState = CardReaderModule.CardReaderModuleStateEnum.Offline;
  328. this.OnModuleStateChange?.Invoke(this, new ModuleStateChangeEventArg(this.cardReaderModules));
  329. var universalApiHub = this.services.GetRequiredService<UniversalApiHub>();
  330. await universalApiHub?.FireEvent(this.context.Processor, GenericAlarm.UniversalApiEventName, new[] {
  331. new GenericAlarm() {
  332. Title = "同一通讯组中的多个(如有)读卡器模块均离线",
  333. Detail=this.deviceConfig.CardReaderModuleConfigs.Select(mc=>$"Module: {mc.Name??""} with address: {mc.PhysicalAddress}").Aggregate("",(acc,n)=>acc+", "+n)+" are offline."
  334. } });
  335. };
  336. #region Load keys
  337. //this.blankCardDefaultRootKey = Enumerable.Repeat<byte>(0x00, 16).ToArray();
  338. //this.newRootKey = Enumerable.Repeat<byte>(0x11, 16).ToArray();
  339. //this.file1DefaultReadKey = Enumerable.Repeat<byte>(0x00, 16).ToArray();
  340. //this.file1DefaultWriteKey = Enumerable.Repeat<byte>(0x00, 16).ToArray();
  341. #endregion
  342. }
  343. public override async Task Process(IContext<byte[], MessageEntity.MessageBase> context)
  344. {
  345. if (!(this.cardReaderModules.FirstOrDefault(b => b.PhysicalAddress == context.Incoming.Message.ModulePhysicalAddress) is CardReaderModule operatingModule))
  346. {
  347. 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");
  348. return;
  349. }
  350. operatingModule.LastIncomingMessageReceivedTime = DateTime.Now;
  351. if (operatingModule.ModuleState == CardReaderModule.CardReaderModuleStateEnum.Offline)
  352. {
  353. operatingModule.ModuleState = CardReaderModule.CardReaderModuleStateEnum.Online;
  354. var readModuleInfoResponse = await this.context.Outgoing.WriteAsync(
  355. new ReadModuleInfoRequest() { ModulePhysicalAddress = operatingModule.PhysicalAddress },
  356. (_, testResponse) => testResponse.ModulePhysicalAddress == operatingModule.PhysicalAddress && (testResponse is GenericFailureResponse || testResponse is ReadModuleInfoResponse), 4000);
  357. if (readModuleInfoResponse is ReadModuleInfoResponse successReadModuleInfoResponse)
  358. {
  359. operatingModule.HardwareFirmware = successReadModuleInfoResponse.ModelAndVersion;
  360. this.logger.LogInformation($"Module (phyAdrs: {operatingModule.PhysicalAddress}, comm: {operatingModule.UnderlyingCommunicatorIdentity}) is online, read module Info: {successReadModuleInfoResponse.ModelAndVersion}");
  361. }
  362. else if (readModuleInfoResponse is GenericFailureResponse failedReadModuleInfoResponse)
  363. {
  364. this.logger.LogInformation($"Module (phyAdrs: {operatingModule.PhysicalAddress}, comm: {operatingModule.UnderlyingCommunicatorIdentity}) is online, but read module info failed due to response: {failedReadModuleInfoResponse.模块返回状态}");
  365. }
  366. else
  367. {
  368. this.logger.LogInformation($"Module (phyAdrs: {operatingModule.PhysicalAddress}, comm: {operatingModule.UnderlyingCommunicatorIdentity}) is online, but read module info timed out");
  369. }
  370. this.OnModuleStateChange?.Invoke(this, new ModuleStateChangeEventArg(operatingModule));
  371. var universalApiHub = this.services.GetRequiredService<UniversalApiHub>();
  372. await universalApiHub.FireEvent(this.context.Processor, GenericAlarm.UniversalApiEventName,
  373. new GenericAlarm[] {
  374. new GenericAlarm()
  375. {
  376. Category = $"单块读卡器模块上线",
  377. Title = $"单块读卡器模块上线",
  378. Detail = $"单块读卡器模块上线, Module: {operatingModule.Name??""} with address: {operatingModule.PhysicalAddress} on comm: {operatingModule.UnderlyingCommunicatorIdentity}",
  379. Severity = GenericAlarmSeverity.Warning
  380. } });
  381. }
  382. if (context.Incoming.Message is ActivateATypeCardResponse activateATypeCardResponse && activateATypeCardResponse.UID.Any())
  383. {
  384. 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...");
  385. this.OnCardTap?.Invoke(this, new CardTapEventArg(activateATypeCardResponse.UID, operatingModule));
  386. var universalApiHub = this.services.GetRequiredService<UniversalApiHub>();
  387. await universalApiHub?.FireEvent(this.context.Processor, OnCardTapEventName,
  388. new
  389. {
  390. Module = this.cardReaderModules.FirstOrDefault(b => b.PhysicalAddress == context.Incoming.Message.ModulePhysicalAddress),
  391. activateATypeCardResponse.UID
  392. });
  393. //var readAppDirsResponse = await this.context.Outgoing.WriteAsync(
  394. // new ReadAppDirsRequest(this.newRootKey) { ModulePhysicalAddress = md.PhysicalAddress },
  395. // (_, testResponse) => testResponse.ModulePhysicalAddress == md.PhysicalAddress && (testResponse is GenericFailureResponse || testResponse is ReadAppDirsResponse), 4000);
  396. //if (readAppDirsResponse is ReadAppDirsResponse successReadAppDirsResponse)
  397. //{
  398. // this.logger.LogInformation($" Read Card AppDirs, count: {successReadAppDirsResponse.DirCount}, " +
  399. // $"dirIds: {successReadAppDirsResponse.DirIds.Select(did => did.Select(i => i.ToString("X2")).Aggregate("0x", (acc, n) => acc + " " + n)).Aggregate((acc, n) => acc + ", " + n)} " +
  400. // $"for module with physical address: {successReadAppDirsResponse.ModulePhysicalAddress}");
  401. //}
  402. //else if (readAppDirsResponse is GenericFailureResponse failedReadAppDirsResponse)
  403. //{
  404. // this.logger.LogInformation($" Read Card AppDirs failed with module code: {failedReadAppDirsResponse.模块返回状态}, cpu code: {failedReadAppDirsResponse.CpuCardState} for module with physical address: {failedReadAppDirsResponse.ModulePhysicalAddress}");
  405. //}
  406. //else
  407. //{
  408. // this.logger.LogInformation($" Read Card AppDirs timed out for module with physical address: {md.PhysicalAddress}");
  409. //}
  410. }
  411. }
  412. public async Task<CardOperationResult[]> FormatCardByResetToNewRootKeyAndBuildDefaultFileStructures(string cardReaderModuleName, byte[] oldRootKeyBytes, byte[] newRootKeyBytes)
  413. {
  414. IEnumerable<CardReaderModule> targetModules;
  415. if (cardReaderModuleName != "*")
  416. targetModules = this.cardReaderModules.Where(m => m.Name == cardReaderModuleName);
  417. else
  418. targetModules = this.cardReaderModules;
  419. if (!targetModules.Any()) return new CardOperationResult[] { new CardOperationResult(404) { ModuleResultCode = $"找不到读卡器, 它的名称是: {cardReaderModuleName ?? ""}" } };
  420. var batchFormatTasks = targetModules.Select(m => m.FormatCardByResetToNewRootKeyAndBuildDefaultFileStructures(m.PhysicalAddress, oldRootKeyBytes.Take(16).ToArray(), newRootKeyBytes.Take(16).ToArray()));
  421. var results = await Task.WhenAll(batchFormatTasks);
  422. return results;
  423. }
  424. public async Task<CardOperationResult[]> WriteCard(string cardReaderModuleName, byte fileId, byte blockAddress, byte[] dataBytes, byte[] writeKeyBytes)
  425. {
  426. IEnumerable<CardReaderModule> targetModules;
  427. if (cardReaderModuleName != "*")
  428. targetModules = this.cardReaderModules.Where(m => m.Name == cardReaderModuleName);
  429. else
  430. targetModules = this.cardReaderModules;
  431. if (!targetModules.Any()) return new CardOperationResult[] { new CardOperationResult(404) { ModuleResultCode = $"找不到读卡器, 它的名称是: {cardReaderModuleName ?? ""}" } };
  432. var batchWriteTasks = targetModules.Select(m => m.WriteCard(m.PhysicalAddress, fileId, blockAddress, dataBytes, writeKeyBytes.Take(16).ToArray()));
  433. var results = await Task.WhenAll(batchWriteTasks);
  434. return results;
  435. }
  436. public async Task<CardOperationResult[]> ReadCard(string cardReaderModuleName, byte fileId, byte blockAddress, byte[] readKeyBytes)
  437. {
  438. IEnumerable<CardReaderModule> targetModules;
  439. if (cardReaderModuleName != "*")
  440. targetModules = this.cardReaderModules.Where(m => m.Name == cardReaderModuleName);
  441. else
  442. targetModules = this.cardReaderModules;
  443. if (!targetModules.Any()) return new CardOperationResult[] { new CardOperationResult(404) { ModuleResultCode = $"找不到读卡器, 它的名称是: {cardReaderModuleName ?? ""}" } };
  444. var batchReadTasks = targetModules.Select(m => m.ReadCard(m.PhysicalAddress, fileId, blockAddress, readKeyBytes.Take(16).ToArray()));
  445. var results = await Task.WhenAll(batchReadTasks);
  446. return results;
  447. }
  448. public async Task<CardOperationResult[]> ReadCardUID(string cardReaderModuleName)
  449. {
  450. IEnumerable<CardReaderModule> targetModules;
  451. if (cardReaderModuleName != "*")
  452. targetModules = this.cardReaderModules.Where(m => m.Name == cardReaderModuleName);
  453. else
  454. targetModules = this.cardReaderModules;
  455. if (!targetModules.Any()) return new CardOperationResult[] { new CardOperationResult(404) { ModuleResultCode = $"找不到读卡器, 它的名称是: {cardReaderModuleName ?? ""}" } };
  456. var batchTasks = targetModules.Select(m => m.ReadCardUID(m.PhysicalAddress));
  457. var results = await Task.WhenAll(batchTasks);
  458. return results;
  459. }
  460. public async Task<CardOperationResult[]> ModifyPredefinedFileKey(string cardReaderModuleName, PredefinedFileKeyTypeEnum fileKeyType, byte[] oldKeyBytes, byte[] newKeyBytes)
  461. {
  462. IEnumerable<CardReaderModule> targetModules;
  463. if (cardReaderModuleName != "*")
  464. targetModules = this.cardReaderModules.Where(m => m.Name == cardReaderModuleName);
  465. else
  466. targetModules = this.cardReaderModules;
  467. if (!targetModules.Any()) return new CardOperationResult[] { new CardOperationResult(404) { ModuleResultCode = $"找不到读卡器, 它的名称是: {cardReaderModuleName ?? ""}" } };
  468. var batchTasks = targetModules.Select(m => m.ModifyPredefinedFileKey(m.PhysicalAddress, fileKeyType, oldKeyBytes.Take(16).ToArray(), newKeyBytes.Take(16).ToArray()));
  469. var results = await Task.WhenAll(batchTasks);
  470. return results;
  471. }
  472. }
  473. }