public async ValueTask <CanPlayResponse> CanPlayAsync(CanPlayRequest value, CallContext context = default) { Guid guid = Guid.NewGuid(); _logger.InfoLog($"[CardService.CanPlayAsync][-IN-] [{guid}] TransactionId {value.TransactionId} | ConcentratorId {value.ConcentratorId} | ControllerId {value.ControllerId} | CardType {value.CardType} | CardId {value.CardId} | ShouldPay {value.ShouldPay} | EndpointRssi: {value.EndpointRssi} | ConcentratorRssi {value.ConcentratorRssi}", GetIpAddress()); // генерира ключ var key = $"{value.ControllerId}-{value.CardId}-{value.TransactionId}-{(value.ShouldPay ? 1 : 0)}"; // извлича тюпъл от кеша за този ключ var cachedItem = CanPlayCacheService.Instance.Get(key, true); // ако има ключ в тюпъла if (!string.IsNullOrWhiteSpace(cachedItem.key)) { // ако има резултат за този ключ в тюпъла if (cachedItem.successResponse != null) { _logger.InfoLog($"[CardService.CanPlayAsync][-OUT-] [{guid}] TransactionId {value.TransactionId} | ConcentratorId {value.ConcentratorId} | ControllerId {value.ControllerId} | CardType {value.CardType} | CardId {value.CardId} | ShouldPay {value.ShouldPay} | EndpointRssi: {value.EndpointRssi} | ConcentratorRssi {value.ConcentratorRssi} | CACHED Result", GetIpAddress()); // връща резултата от тюпъла return(cachedItem.successResponse); } } // резервира семафора за текущия тред и продължава, други тредове стигнали до тук чакат ... await semaphoreSlim.WaitAsync(); // проверява за евнтуален тюпъл и резултат за този ключ от други тредове cachedItem = CanPlayCacheService.Instance.Get(key); if (cachedItem.successResponse != null) { _logger.InfoLog($"[CardService.CanPlayAsync][-OUT-] [{guid}] TransactionId {value.TransactionId} | ConcentratorId {value.ConcentratorId} | ControllerId {value.ControllerId} | CardType {value.CardType} | CardId {value.CardId} | ShouldPay {value.ShouldPay} | EndpointRssi: {value.EndpointRssi} | ConcentratorRssi {value.ConcentratorRssi} | CACHED Result", GetIpAddress()); // освобождава семафора за следващи тредове, които чакат semaphoreSlim.Release(); // връща резултата от тюпъла return(cachedItem.successResponse); } // резервира ключа в кеша, като създава тюпъл с този ключ, но без резултат CanPlayCacheService.Instance.Add(key, (key, null)); CanPlayResponse response = null; try { // извлича резултата от апи-то (отнема много време) var tupleResult = await CanPlayAsyncInternal(guid, value.ConcentratorId, value.ControllerId, value.CardType, value.CardId, value.ShouldPay, value.TransactionId, value.EndpointRssi, value.ConcentratorRssi); if (tupleResult.success) { // обновява резултата в тюпъла в кеша за резервирания ключ CanPlayCacheService.Instance.Add(key, (key, tupleResult.response)); } // връща резултата от апи-то response = tupleResult.response; } finally { // освобождава семафора за следващи тредове, които чакат semaphoreSlim.Release(); } return(response ?? new CanPlayResponse { TransactionId = value.TransactionId, Time = DateTime.UtcNow, ResponseCode = 109, ConcentratorId = value.ConcentratorId, ControllerId = value.ControllerId, CardType = value.CardType, CardId = value.CardId, CardNumber = string.Empty, ServiceId = string.Empty, ServiceName = string.Empty, Permission = false, RelayType = string.Empty, RelayPulse = 0, RelayOnTime = 0, RelayOffTime = 0, RelayDisplayTime = 0, DisplayLine1 = "", DisplayLine2 = "" }); }
// метода се извиква при всяко запитване на конектнатия клиент public byte[] ProccessRequest(byte[] message) { //извличане на входните данни var splittedMessage = SplitByteArray(message, 0x1F).ToArray(); if (splittedMessage.Count() == 0) { return new byte[] { 0x45, 0x52, 0x52, 0x4F, 0x52 } } ; //ERROR // how many tokens int tokensCount = splittedMessage.GetLength(0); // command string cmd = tokensCount >= 1 ? new string( splittedMessage[0] .Where(b => b != 0x1F && b != 0x1E) // except: 0x1F - unit separator char(31), 0x1E - record separator char(30) .Select(b => (char)b) .ToArray()) : string.Empty; if (cmd.Equals("RG", StringComparison.InvariantCultureIgnoreCase)) { CanPlayRequest request = CanPlayRequest.DeserializeASCII(splittedMessage); _logger.LogInformation(System.Text.Json.JsonSerializer.Serialize(request)); Proto.CanPlayRequest proto_request = request.ToProtoCanPlayRequest(); CanPlayResponse response; try { Proto.CanPlayResponse proto_response = _cardService.CanPlayAsync(proto_request).Result; if (proto_response != null) { response = CanPlayResponse.FromProtoCanPlayResponse(proto_response); } else { response = new CanPlayResponse { Success = false, MessageLine1 = "Err 109", MessageLine2 = "Internal server" }; _logger.LogError("TextMessageService.ProccessRequest => Proto.CanPlayResponse is null"); } } catch (Exception ex) { response = new CanPlayResponse { Success = false, MessageLine1 = "Err 109", MessageLine2 = "Internal server" }; _logger.LogError($"TextMessageService.ProccessRequest throws: {ex}"); } _logger.LogInformation(System.Text.Json.JsonSerializer.Serialize(response)); return(response.SerializeASCII()); } else if (cmd.Equals("RP", StringComparison.InvariantCultureIgnoreCase)) { ServicePriceRequest request = ServicePriceRequest.DeserializeASCII(splittedMessage); _logger.LogInformation(System.Text.Json.JsonSerializer.Serialize(request)); Proto.ServicePriceRequest proto_request = request.ToProtoServicePriceRequest(); ServicePriceResponse response; try { Proto.ServicePriceResponse proto_response = _cardService.ServicePriceAsync(proto_request).Result; if (proto_response != null) { response = ServicePriceResponse.FromProtoServicePriceResponse(proto_response); } else { response = new ServicePriceResponse { Success = false, MessageLine1 = "Err 109", MessageLine2 = "Internal server" }; _logger.LogError("TextMessageService.ProccessRequest => Proto.ServicePriceRequest is null"); } } catch (Exception ex) { response = new ServicePriceResponse { Success = false, MessageLine1 = "Err 109", MessageLine2 = "Internal server" }; _logger.LogError($"TextMessageService.ProccessRequest throws: {ex}"); } _logger.LogInformation(System.Text.Json.JsonSerializer.Serialize(response)); return(response.SerializeASCII()); } else if (cmd.Equals("T", StringComparison.InvariantCultureIgnoreCase)) { return(Encoding.ASCII.GetBytes($"{DateTime.Now:dd.MM.yyyy HH:mm}\x1E")); } else { return(Encoding.ASCII.GetBytes($"{DateTime.Now:dd.MM.yyyy HH:mm}\x1E")); } }
private async ValueTask <(bool success, CanPlayResponse response)> CanPlayAsyncInternal( Guid guid, string concentratorId, string controllerId, int cardType, string cardId, bool shouldPay, int transactionId, int endpointRssi, int concentratorRssi) { Concentrator getConcentrator() { var repo = _serviceProvider.GetService <ICardSystemRepository <Concentrator> >(); return(repo.AsNoTracking().FirstOrDefault(x => x.DeviceId == concentratorId)); }; ControllerConfigRelay getController() { var repo = _serviceProvider.GetService <ICardSystemRepository <ControllerConfigRelay> >(); return(repo.AsNoTracking() .Include(x => x.EGM) .ThenInclude(x => x.AllowedCurrencies) .FirstOrDefault(x => x.SerialNumber == controllerId && x.OwnerId == _ownerId)); }; Card getCard() { Card entity = null; ICardSystemRepository <Card> repo = null; string cardDBId = CardCacheServiceV2.Instance.GetIdByExternalId(cardId); if (!string.IsNullOrWhiteSpace(cardDBId)) { entity = CardCacheServiceV2.Instance.GetCard(_serviceProvider, cardDBId); if (entity != null) { return(entity); } repo = _serviceProvider.GetService <ICardSystemRepository <Card> >(); entity = repo.AsNoTracking() .Include(x => x.CardMode) .Include(x => x.User) .FirstOrDefault(x => x.Id == cardDBId); if (entity != null) { CardCacheServiceV2.Instance.AddCard(_serviceProvider, entity); CardCacheServiceV2.Instance.AddNewMap(entity); } return(entity); } repo = _serviceProvider.GetService <ICardSystemRepository <Card> >(); entity = repo.AsNoTracking() .Include(x => x.CardMode) .Include(x => x.User) .FirstOrDefault(x => x.ExternalId == cardId); if (entity != null) { CardCacheServiceV2.Instance.AddCard(_serviceProvider, entity); CardCacheServiceV2.Instance.AddNewMap(entity); } return(entity); }; IEnumerable <CurrencyTemplate> getCurrencyTemplates() { var repo = _serviceProvider.GetService <ICardSystemRepository <CurrencyTemplate> >(); return(repo.AsNoTracking() .Where(x => x.OwnerId == _ownerId) .ToList()); } var concentrator = getConcentrator(); if (concentrator == null) { var response = new CanPlayResponse { TransactionId = transactionId, Time = DateTime.UtcNow, ResponseCode = 101, ConcentratorId = concentratorId, ControllerId = controllerId, CardType = cardType, CardId = cardId, CardNumber = string.Empty, ServiceId = string.Empty, ServiceName = string.Empty, Permission = false, RelayType = string.Empty, RelayPulse = 0, RelayOnTime = 0, RelayOffTime = 0, RelayDisplayTime = 0, DisplayLine1 = "Err 101", DisplayLine2 = "Concentrator N/A" }; _logger.ErrorLog($"[CardService.CanPlayAsyncInternal][-OUT-] [{guid}] TransactionId {transactionId} | ConcentratorId {concentratorId} | ControllerId {controllerId} | CardType {cardType} | CardId {cardId} | ShouldPay {shouldPay} | EndpointRssi: {endpointRssi} | ConcentratorRssi {concentratorRssi} | {JsonSerializer.Serialize(response, new JsonSerializerOptions { WriteIndented = false })}", GetIpAddress()); return(false, response); } var controller = getController(); if (controller == null) { var response = new CanPlayResponse { TransactionId = transactionId, Time = DateTime.UtcNow, ResponseCode = 102, ConcentratorId = concentratorId, ControllerId = controllerId, CardType = cardType, CardId = cardId, CardNumber = string.Empty, ServiceId = string.Empty, ServiceName = string.Empty, Permission = false, RelayType = string.Empty, RelayPulse = 0, RelayOnTime = 0, RelayOffTime = 0, RelayDisplayTime = 0, DisplayLine1 = "Err 102", DisplayLine2 = "Controller N/A" }; _logger.ErrorLog($"[CardService.CanPlayAsyncInternal][-OUT-] [{guid}] TransactionId {transactionId} | ConcentratorId {concentratorId} | ControllerId {controllerId} | CardType {cardType} | CardId {cardId} | ShouldPay {shouldPay} | EndpointRssi: {endpointRssi} | ConcentratorRssi {concentratorRssi} | {JsonSerializer.Serialize(response, new JsonSerializerOptions { WriteIndented = false })}", GetIpAddress()); return(false, response); } var card = getCard(); if (card == null) { var response = new CanPlayResponse { TransactionId = transactionId, Time = DateTime.UtcNow, ResponseCode = 103, ConcentratorId = concentratorId, ControllerId = controllerId, CardType = cardType, CardId = cardId, CardNumber = string.Empty, ServiceId = string.Empty, ServiceName = string.Empty, Permission = false, RelayType = string.Empty, RelayPulse = 0, RelayOnTime = 0, RelayOffTime = 0, RelayDisplayTime = 0, DisplayLine1 = "Err 103", DisplayLine2 = "Card N/A" }; _logger.ErrorLog($"[CardService.CanPlayAsyncInternal][-OUT-] [{guid}] TransactionId {transactionId} | ConcentratorId {concentratorId} | ControllerId {controllerId} | CardType {cardType} | CardId {cardId} | ShouldPay {shouldPay} | EndpointRssi: {endpointRssi} | ConcentratorRssi {concentratorRssi} | {JsonSerializer.Serialize(response, new JsonSerializerOptions { WriteIndented = false })}", GetIpAddress()); return(false, response); } var currencyTemplates = getCurrencyTemplates(); IEnumerable <ScheduleDay> getScheduleDays() { IEnumerable <ScheduleDay> egmPriceModifiers = null; var repo = _serviceProvider.GetService <ICardSystemRepository <ScheduleDay> >(); if (!string.IsNullOrWhiteSpace(controller.EGM.Id)) { egmPriceModifiers = repo.AsNoTracking() .Include(x => x.Hours) .Where(x => x.EGMId == controller.EGM.Id) .AsEnumerable(); } if (egmPriceModifiers != null && !egmPriceModifiers.Any()) { egmPriceModifiers = repo .AsNoTracking() .Include(x => x.Hours) .Where(x => x.GameCenterId == controller.EGM.GameCenterId) .AsEnumerable(); } return(egmPriceModifiers ?? new List <ScheduleDay>()); } var scheduleDays = getScheduleDays(); // ако се изиква плащане този метод променя баланса на картата var canPlayDto = _serviceProvider.GetService <ICanPlayService>().CanPlayV2( _serviceProvider, concentrator, controller, card, scheduleDays.ToList(), shouldPay, currencyTemplates); if (canPlayDto.HasError) { var errors = canPlayDto.Message.Split('|'); var response = new CanPlayResponse { TransactionId = transactionId, Time = DateTime.UtcNow, ResponseCode = int.Parse(errors[0]), ConcentratorId = concentratorId, ControllerId = controllerId, CardType = cardType, CardId = cardId, CardNumber = string.Empty, ServiceId = string.Empty, ServiceName = string.Empty, Permission = false, RelayType = string.Empty, RelayPulse = 0, RelayOnTime = 0, RelayOffTime = 0, RelayDisplayTime = 0, DisplayLine1 = $"Err {errors[0]}", DisplayLine2 = errors[1] }; _logger.ErrorLog($"[CardService.CanPlayAsyncInternal][-OUT-] [{guid}] TransactionId {transactionId} | ConcentratorId {concentratorId} | ControllerId {controllerId} | CardType {cardType} | CardId {cardId} | ShouldPay {shouldPay} | EndpointRssi: {endpointRssi} | ConcentratorRssi {concentratorRssi} | {JsonSerializer.Serialize(response, new JsonSerializerOptions { WriteIndented = false })}", GetIpAddress()); return(false, response); } EGM gameDto = null; if (canPlayDto.CanPlay) { gameDto = controller.EGM; } var canPlayResponse = new CanPlayResponse { TransactionId = transactionId, Time = DateTime.UtcNow, ResponseCode = 0, ConcentratorId = concentrator.DeviceId, ControllerId = controller.SerialNumber, CardType = card.Type, CardId = card.ExternalId, CardNumber = card.Number, ServiceId = canPlayDto.GameId, ServiceName = canPlayDto.GameName, Permission = canPlayDto.CanPlay, RelayType = gameDto?.RelayMode, RelayPulse = gameDto?.RelayPulse ?? 0, RelayOnTime = gameDto?.RelayOnTime ?? 0, RelayOffTime = gameDto?.RelayOffTime ?? 0, RelayDisplayTime = gameDto?.RelayDisplayTime ?? 0, DisplayLine1 = $"{canPlayDto.PricePerGame:F2}".Trim().Trim(',').Trim(), DisplayLine2 = $"{canPlayDto.RemainingBalance:F2}" }; // само ако се изисква плащане логира плащането в транзакшън лога и обновява кеша на картата в api.icardmanager.eu if (shouldPay) { canPlayDto.GameId = gameDto?.Id; string relayInfo = $" | Relay ontime: {gameDto?.RelayOnTime}, Relay offtime: {gameDto?.RelayOffTime}, Relay pulse: {gameDto?.RelayPulse}, Relay display time: {gameDto?.RelayDisplayTime}, Relay mode: {gameDto?.RelayMode}, Game Serial Number: '{gameDto?.SerialNumber}'"; CreateCanPlayLog( concentrator.DeviceId, controller.SerialNumber, $"{canPlayDto.GameCenterName}{relayInfo}", canPlayDto.GameId, canPlayDto.GameName, canPlayDto.CurrencyInfo, card.ExternalId, card.Type, canPlayDto.PricePerGame, canPlayDto.PaidFromBonusBalance, canPlayDto.CanPlay, gameDto?.GameCenterId, gameDto != null ? gameDto.OwnerId : "system" ); try { var apiResponse = await _restApiClient.GetAsync <string>(_configuration.GetSection("AppSettings:ApiServiceURL").Get <string>(), $"api/{_ownerId}/card/{card.Id}/reloadcache"); if (apiResponse.hasError) { _logger.ErrorLog($"[CardService.CanPlayAsyncInternal][-API-] [{guid}] TransactionId {transactionId} | ConcentratorId {concentratorId} | ControllerId {controllerId} | CardType {cardType} | CardId {cardId} | ShouldPay {shouldPay} | EndpointRssi: {endpointRssi} | ConcentratorRssi {concentratorRssi} | {apiResponse.errorMessage}", GetIpAddress()); } } catch (Exception ex) { _logger.LogException($"CardService.CanPlayAsyncInternal throws: {ex}"); } } _logger.InfoLog($"[CardService.CanPlayAsyncInternal][-OUT-] [{guid}] TransactionId {transactionId} | ConcentratorId {concentratorId} | ControllerId {controllerId} | CardType {cardType} | CardId {cardId} | ShouldPay {shouldPay} | EndpointRssi: {endpointRssi} | ConcentratorRssi {concentratorRssi} | DB Result {JsonSerializer.Serialize(canPlayResponse, new JsonSerializerOptions { WriteIndented = false })}", GetIpAddress()); return(true, canPlayResponse); }