Проблема многопоточности C# при одновременной печати смарт-карт на нескольких принтерах Evolis Primacy 2C#

Место общения программистов C#
Ответить
Anonymous
 Проблема многопоточности C# при одновременной печати смарт-карт на нескольких принтерах Evolis Primacy 2

Сообщение Anonymous »

Я разрабатываю приложение C#, которое должно обеспечивать одновременную печать на четырех принтерах Evolis Primacy 2 с кодировщиками Elyctis. Каждый принтер должен записывать данные в чипы карт и печатать на картах совершенно независимо и без конфликтов.
Текущая реализация использует Task.Run для обработки пакетов, но у меня возникают конфликты ресурсов, взаимоблокировки и проблемы с памятью, когда несколько принтеров работают одновременно.
Основной метод пакетной обработки:

Код: Выделить всё

private async void BTNPERSOLOT_Click(object sender, EventArgs e)
{
BTNPERSOLOT.Enabled = false;

List
 cartesAImprimer = listeFiltreePerso
.Take(FenêtrePrincipale.nombreCarteToPrint)
.Select(c => (Personnalisation)c.Clone())
.ToList();

string nomImprimante = CB_Printerss.SelectedItem.ToString();

listeFiltreePerso = listeFiltreePerso.Skip(FenêtrePrincipale.nombreCarteToPrint).ToList();
bindingSource.DataSource = listeFiltreePerso;
InitialiserDataGridViewPersonnalisation(listeFiltreePerso);

_ = Task.Run(async () =>
{
try
{
Device device = Evolis.Evolis.GetDevices().FirstOrDefault(p =>
string.Equals(p.name, nomImprimante, StringComparison.OrdinalIgnoreCase));

string[] lecteursEnregistres = ws.getReadersByPrinter(nomImprimante);
string[] lecteursValides = await GetValidPCSCReaders(lecteursEnregistres);

await impressionSimultanee(cartesAImprimer, device, lecteursValides);

this.Invoke((MethodInvoker)delegate
{
MessageBox.Show(this,
$"Lot of {cartesAImprimer.Count} cards for printer {nomImprimante} completed.",
"Batch Done", MessageBoxButtons.OK, MessageBoxIcon.Information);
});
}
catch (Exception ex)
{
this.Invoke((MethodInvoker)delegate
{
MessageBox.Show(this,
$"Error in batch for printer {nomImprimante}:\n\n{ex.Message}",
"Batch Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
});
}
});

await Task.Delay(500);
BTNPERSOLOT.Enabled = true;
}
Логика одновременной печати:

Код: Выделить всё

private async Task impressionSimultanee(List
 cartesAImprimer, Device imprimante, string[] lecteursImprim)
{
int successCount = 0;
List lignesNonEcrites = new List();

foreach (var carte in cartesAImprimer)
{
PrintTools printTool = null;
ConnexionResult resultatConnexionPuce = null;

try
{
printTool = new PrintTools();

resultatConnexionPuce = await PrintTools.connexionPuceAsync(imprimante, lecteursImprim);
if (!resultatConnexionPuce.IsSuccess)
{
throw new Exception("Chip connection failed.");
}

ModelResponse isWrite = PrintTools.ecriturePuce(carte, imprimante, lecteursImprim, resultatConnexionPuce.ConnectionObject);
if (!isWrite.status)
{
throw new Exception($"Chip writing failed: {isWrite.message}");
}

if (DateTime.TryParse(carte.dateNaissance, out DateTime dateN))
carte.dateNaissance = dateN.ToString("dd/MM/yyyy");

if (imprimante.model.ToString().Equals("Evolis_Primacy_2"))
{
Retour retour = await printTool.imprimerCarteEvolis(carte, imprimante.name);
if (!retour.IsSuccess)
{
throw new Exception("Graphic printing error!");
}
}

Personnalisation perso = await carteLocalRepository.GetPersonnalisationByNinLocalAsync(carte.nin);
if (perso != null)
{
perso.statutCarte = 2;
perso.numeroSerie = resultatConnexionPuce.NumeroSerie;
perso.login = User.login;
perso.dateDelivrance = DateTime.Now.ToString("yyyy-MM-dd");
perso.dateExpiration = DateTime.Now.AddYears(10).ToString("yyyy-MM-dd");

ResponseData reponsePersonnalisation = await ConnexionDB.updatePersonnalisation(perso);
if (reponsePersonnalisation != null &&  !reponsePersonnalisation.status)
{
lignesNonEcrites.Add($"{carte.prenom} {carte.nom} (DB Update Error: {reponsePersonnalisation.msg})");
}
}

successCount++;
}
catch (Exception ex)
{
lignesNonEcrites.Add($"{carte.prenom} {carte.nom} (Error: {ex.Message})");
PrintTools.execPrinterCmd("Se");
}
finally
{
if (printTool != null)
{
try { printTool = null; } catch { }
}

if (resultatConnexionPuce != null)
{
try { resultatConnexionPuce.ConnectionObject?.Disconnect(DISCONNECT.Leave); } catch { }
try { resultatConnexionPuce.m_iResMan?.ReleaseContext(); } catch { }
}
}
}

if (lignesNonEcrites.Count > 0)
{
string erreurs = string.Join("\n", lignesNonEcrites);
throw new Exception($"Batch completed with {lignesNonEcrites.Count} failure(s):\n{erreurs}");
}
}
Способ подключения чипа:

Код: Выделить всё

public static async Task connexionPuceAsync(Device choosenPrinter, string[] lecteursImprimante)
{
var result = new ConnexionResult { IsSuccess = false };
ISCardManager m_iResMan = null;
ISCardConnection m_iCard = null;
bool connected = false;

if (choosenPrinter.model.ToString().Equals("Evolis_Primacy_2"))
{
Connection co = null;
try
{
co = new Connection(choosenPrinter.name);
if (co.IsOpen())
{
string retour = co.SendCommand("Sis");
}
}
finally
{
co?.Close();
}
}

try
{
m_iResMan = new SCardManager();
m_iResMan.EstablishContext(SCOPE.System);

foreach (string lecteur in lecteursImprimante)
{
try
{
m_iCard = m_iResMan.CreateConnection(lecteur);
m_iCard.Connect(SHARE.Shared, PROTOCOL.T0orT1);

if (m_iCard.Connected)
{
connected = true;
break;
}
}
catch
{
try { m_iCard?.Disconnect(DISCONNECT.Leave); } catch { }
m_iCard = null;
}
}

if (!connected)
{
throw new Exception("No card found in printer readers");
}

ReadService readService = new ReadService(m_iCard);
ModelResponse reponseNumeroSerie = readService.getSerailNumber();

if (string.IsNullOrEmpty(reponseNumeroSerie?.message))
{
throw new Exception("Cannot read card serial number");
}

result.NumeroSerie = reponseNumeroSerie.message;
bool isCardPresente = await carteLocalRepository.CheckCarteExistsByNumeroSerieLocalAsync(result.NumeroSerie);

if (!isCardPresente)
{
Webservice ws = new Webservice();
isCardPresente = ws.CheckCarteExistsByNumeroSerie(result.NumeroSerie);
}

if (!isCardPresente)
{
throw new Exception($"Card (No.  {result.NumeroSerie}) not found in database!");
}

result.IsSuccess = true;
result.ConnectionObject = m_iCard;
result.m_iResMan = m_iResMan;

return result;
}
catch (Exception exc)
{
try { m_iCard?.Disconnect(DISCONNECT.Leave); } catch { }
try { m_iResMan?.ReleaseContext(); } catch { }
throw new Exception("Error during card reading/verification: " + exc.Message, exc);
}
}
Метод записи чипа:

Код: Выделить всё

public static ModelResponse ecriturePuce(Personnalisation personnalisation, Device choosenPrinter, string[] lecteursImprimante, ISCardConnection ConnectionObject)
{
ModelResponse valeurRetour = new ModelResponse { status = false, message = "Error!", code = 0 };

try
{
if (personnalisation?.visage != null)
{
string compressedBase64 = compressImage(personnalisation.visage);
if (Base64ToHex(personnalisation.visage).Length > 75534)
{
personnalisation.visage = compressedBase64;
}
}

if (personnalisation?.signature != null)
{
string compressedBase64 = compressSignature(personnalisation.signature);
if (Base64ToHex(personnalisation.signature).Length > 34000)
{
personnalisation.signature = compressedBase64;
}
}

if (!ConnectionObject.Connected)
{
throw new Exception("Writing impossible: No card found.");
}

if (validation(personnalisation))
{
WriteService writeService = new WriteService(ConnectionObject);
ModelResponse isInitialize = writeService.initialisationStructure();

if (isInitialize.status)
{
DG1File dg1 = new DG1File();
dg1.registrationNumber = personnalisation.numeroEnregistrement ?? " ";
dg1.pensionerNumber = personnalisation.numeroPensionnaire ?? " ";
dg1.phoneNumber = personnalisation.telephone ?? " ";
dg1.idNumber = personnalisation.nin ?? " ";
dg1.prenom = personnalisation.prenom ?? " ";
dg1.nom = personnalisation.nom ?? " ";
dg1.sexe = !string.IsNullOrEmpty(personnalisation.sexe) ? personnalisation.sexe[0].ToString() : " ";
dg1.prenomPere = personnalisation.prenomPere ?? " ";
dg1.prenomMere = personnalisation.prenomMere ?? " ";
dg1.nomMere = personnalisation.nomMere ?? " ";
dg1.dateNaissance = dateFormated(personnalisation.dateNaissance) ?? " ";
dg1.lieuNaissance = personnalisation.lieuNaissance ?? " ";

DG2File dg2 = new DG2File();
dg2.dateDelivrance = DateTime.Now.ToString("dd-MM-yyyy");
personnalisation.dateDelivrance = dg2.dateDelivrance;
dg2.dateExpiration = DateTime.Now.AddYears(10).ToString("dd-MM-yyyy");
personnalisation.dateExpiration = dg2.dateExpiration;
dg2.dateDerniereLecture = DateTime.Now.ToString("dd-MM-yyyy");

DG3File dg3 = new DG3File();
if (personnalisation.signature == null)
{
dg3.neSaitPasSigner = true;
dg2.tailleSignature = "0";
}
else
{
dg3.neSaitPasSigner = false;
dg3.signature = personnalisation.signature;
dg2.tailleSignature = Base64ToHex(personnalisation.signature).Length.ToString();
}

DG4File dg4 = new DG4File();
dg4.etatMainGauche = true;
dg4.etatMainDroite = true;

Finger[] fingers = new Finger[10];
for (int i = 1; i 
{
using (var connection = new SQLiteConnection(ConnexionDB.localConnectionString))
{
await connection.OpenAsync();

string query = @"SELECT * FROM impression WHERE LOWER(nin) = LOWER(@nin) LIMIT 1";

using (var cmd = new SQLiteCommand(query, connection))
{
cmd.Parameters.AddWithValue("@nin", nin);

using (var reader = await cmd.ExecuteReaderAsync())
{
bool hasRecord = await reader.ReadAsync();
if (!hasRecord) return null;

return new Personnalisation
{
id = reader["id"] != DBNull.Value ? Convert.ToInt32(reader["id"]) : 0,
numeroEnregistrement = GetSafeString(reader, "numero_enregistrement"),
statutCarte = GetSafeInt(reader, "statut_carte"),
numeroSerie = GetSafeString(reader, "numero_serie"),
nin = GetSafeString(reader, "nin"),
numeroPensionnaire = GetSafeString(reader, "numero_pensionnaire"),
prenom = GetSafeString(reader, "prenom"),
nom = GetSafeString(reader, "nom"),
dateNaissance = GetSafeString(reader, "date_naissance"),
lieuNaissance = GetSafeString(reader, "lieu_naissance"),
sexe = GetSafeString(reader, "sexe"),
nomMere = GetSafeString(reader, "nom_mere"),
prenomMere = GetSafeString(reader, "prenom_mere"),
prenomPere = GetSafeString(reader, "prenom_pere"),
telephone = GetSafeString(reader, "telephone"),
dateDelivrance = GetSafeString(reader, "date_delivrance"),
dateExpiration = GetSafeString(reader, "date_expiration"),
visage = GetSafeString(reader, "visage"),
signature = GetSafeString(reader, "signature")
};
}
}
}
});

return result;
}
catch (Exception ex)
{
Console.WriteLine($"[ERROR] GetPersonnalisationByNinLocalAsync: {ex.Message}");
return null;
}
}

public static Task  updatePersonnalisation(Personnalisation p)
{
return DbTaskQueue.Enqueue(async () =>
{
var response = new ResponseData();

try
{
using (var connection = new SQLiteConnection(ConnexionDB.localConnectionString))
{
await connection.OpenAsync();

using (var transaction = connection.BeginTransaction())
{
try
{
using (var checkCmd = new SQLiteCommand(
"SELECT COUNT(*) FROM impression WHERE id=@id", connection))
{
checkCmd.Parameters.AddWithValue("@id", p.id);
if (Convert.ToInt32(await checkCmd.ExecuteScalarAsync()) == 0)
{
response.status = false;
response.msg = "Record not found.";
return response;
}
}

if (string.IsNullOrWhiteSpace(p.numeroSerie))
{
response.status = false;
response.msg = "Missing serial number.";
return response;
}

using (var updateCmd = new SQLiteCommand(
@"UPDATE impression SET
statut_carte=@statut,
date_modification=@modification,
login=@login,
date_delivrance=@delivrance,
date_expiration=@expiration,
numero_serie=@serie
WHERE id=@id", connection))
{
updateCmd.Parameters.AddWithValue("@statut", p.statutCarte);
updateCmd.Parameters.AddWithValue("@modification", DateTime.UtcNow);
updateCmd.Parameters.AddWithValue("@login", p.login);
updateCmd.Parameters.AddWithValue("@delivrance", p.dateDelivrance);
updateCmd.Parameters.AddWithValue("@expiration", p.dateExpiration);
updateCmd.Parameters.AddWithValue("@serie", p.numeroSerie);
updateCmd.Parameters.AddWithValue("@id", p.id);

await updateCmd.ExecuteNonQueryAsync();
}

using (var cardUpdateCmd = new SQLiteCommand(
@"UPDATE carte_local SET
login=@login,
etat=@etat,
dateModification=@modification
WHERE numeroSerie=@serie", connection))
{
cardUpdateCmd.Parameters.AddWithValue("@login", p.login);
cardUpdateCmd.Parameters.AddWithValue("@etat", p.statutCarte);
cardUpdateCmd.Parameters.AddWithValue("@modification", DateTime.UtcNow);
cardUpdateCmd.Parameters.AddWithValue("@serie", p.numeroSerie);

await cardUpdateCmd.ExecuteNonQueryAsync();
}

transaction.Commit();
response.status = true;
response.msg = "Update successful.";
}
catch
{
transaction.Rollback();
throw;
}
}
}
}
catch (Exception ex)
{
response.status = false;
response.msg = ex.Message;
}

return response;
});
}

public static class DbTaskQueue
{
private static readonly SemaphoreSlim _dbSemaphore = new SemaphoreSlim(4, 4); // 4 accès concurrents

public static async Task Enqueue(Func  taskGenerator)
{
await _dbSemaphore.WaitAsync();
try
{
Console.WriteLine($"[DbTaskQueue] Sémaphore acquis - Restant: {_dbSemaphore.CurrentCount}");
var result = await taskGenerator();
return result;
}
finally
{
_dbSemaphore.Release();
Console.WriteLine($"[DbTaskQueue] Sémaphore libéré - Restant: {_dbSemaphore.CurrentCount}");
}
}

public static async Task Enqueue(Func action)
{
await _dbSemaphore.WaitAsync();
try
{
await action();
}
finally
{
_dbSemaphore.Release();
}
}
}
Основные проблемы:
Конфликты ресурсов между соединениями PCSC на нескольких принтерах.
Утечки памяти при обработке изображений и подключениях карт.
Одновременный доступ к аппаратным ресурсам принтера.
Проблемы безопасности потоков с общим контекстом PCSC.
Правильное использование подключений карт и ресурсов изображений.>

Подробнее здесь: https://stackoverflow.com/questions/798 ... multiple-e
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «C#»