Dzien dobry,
Mam pytanie dotyczace wywolywania subskrybcji eventow. Aby latwiej zrozumiec problem,przedstawiam ponizej przyklad aplikacji.
Jest to uproszczony przyklad (I tylko czesc aplikacji), wiec prosze sie nie czepiac skaldni, chodzi bardziej o logike zagadnenia.
Aplikacja odbiera request z OPC servera (z PLC), na podstawie tego wyszukuje danych w bazie danych (niekoniecznie na tej samej maszynie) i wysyla te dane do OPC servera (do PLC)
Mam trzy klasy classy.
OPCInterface - odbiera i wysyla dane dane z/do servera OPC
DataMgr - just for track data flow
SQLInterface - obsluga DB
public class OPCServerInterface {
private void DataChangedHandler(object sender, DataChangeEventArgs e)
{
for (int i = 0; i < e.sts.Length; ++i)
{
if (HRESULTS.Succeeded(e.sts[i].Error))
{
_ReadItems[e.sts[i].HandleClient].Value = e.sts[i];
OnReceivedNewData(); // raise event 1
}
}
protected virtual void OnReceivedNewData()
{
_EvenArgs.ReadItems = _ReadItems;
RecivedNewData?.Invoke(this, _EvenArgs);
}
}
Callback DataChangeHandler podnosi event 1 OnReceiveNewData, kiedy nowe dane przyjda z OPC
Dane w _EvenArgs sa wysylane do DataMgr.
public class DataMgr
{
public void OnReceivedRequestFromPlc(object o, ReadDataEventArg e)
{
ActualData.Id = ++_Id;
if (_Id > 99999) _Id = 0;
ActualData.PlcRequest = e.ReadItems[_PlcIndex].Value.DataValue.ToString();
_LogMsg(true, "ID=" + ActualData.Id.ToString("00000") + " PLC Request: "+ ActualData.PlcRequest);
OnReceivedNewRequest(); //** raise event 2**
}
protected virtual void OnReceivedNewRequest()
{
_PlcRequestEventArg.Request = ActualData.PlcRequest;
RecivedPlcRequest?.Invoke(this, _PlcRequestEventArg);
}
}
Jak DataMgr odbierze dane to podnosi event 2 OnReceivedNewRequest, ktory przekazuje dane do SQLInterface
public class SQLInterface
{
public void OnReceivedSearchCommand(object o, SearchCommnadEventArg e)
{
_DataRequest = e.Request.Trim();
LogMsg(_LogProcessName, _ExtDiag,"Received new search command: " + _DataRequest);
if (_SearchDataThread != null) { while (_SearchDataThread.IsAlive) { } }
_SearchDataThread = new Thread(new ThreadStart(GetDataFromDB));
_SearchDataThread.IsBackground = true;
_SearchDataThread.Priority = ThreadPriority.Lowest;
_SearchDataThread.Start();
}
}
Jak SQL interface odbierze dane z DataMgr, to uruchamia nowy watek _SerchDataThread, ktory wyszukuje dane w DB
public void GetDataFromDB()
{
//...
try
{
if (reader.Read())
{
if (!reader.IsDBNull(0))
{
_DataFoundEvenArg.Data = ((string)reader["Data"]).Trim();
LogMsg(_LogProcessName, _ExtDiag, "SQL: Data found: " + _DataFoundEvenArg.Data);
OnDataFound(); // raise event 3
}
}
else
{
_DataFoundEvenArg.Data = "99999999999999999999999999999";
LogMsg(_LogProcessName, _ExtDiag, "Data not exist for: " + _cmdRead.Parameters["@PatternData"].Value);
}
}
catch (Exception ex)
{
LogMsg("EXCE", true, "Read data: " + _DataRequest + " from DB failed: " + ex.Message);
}
finally
{
// Always call Close when done reading.
reader.Close();
}
}
jak znajdzie dane to podnosi event 3 OnDataFound() ktory przekazuje dane do DataMgr, ten podnosi event 4 ktory przekazuje dane do wyslania przez OPC.
Z grubsza przeplyw danych jest nastepujacy:
OPC callback podnosi event 1 - > DataMgr odbiera dane, podnosi event 2 -> SQL odbiera dane, uruchamia nowy watek, watek podnosi event 3 -> DataMgr odbiera dane podnosi event 4 -> OPC odbiera dane za pomoca RecData().
Teraz gdzie jest problem.
W RecData(0 mialem blad. Blad zostal wychwycone przez exception w SQL thread.
catch (Exception ex)
{
LogMsg("EXCE", true, "Read data: " + _DataRequest + " from DB failed: " + ex.Message);
}
oznacza to ze watek z SQL jest "trzymany" az do zakonczenia RecData(). Na podobnej zasadzie spodziewam sie ze callback z OPC ( z pierwszego kroku) jest "trzymany" az do zakonczenia ( w tym wypadku do wystartowanie watku _SearchDataThread) , czego chcialem uniknac ;(.
Co zrobic zeby "zwalniac klasy" bezposrednio po podniesieniu eventu?
Czy jedynym rozwiazaniem jest w wywolanie nowego watku w subscrypcji, a dopiero ten watek podniesie nastepny event? Tak jak to zrobilem w SQLInterface (chociaz z innych przyczyn ;))
PS. Podnoszenie event 3 przenislem do finally
finally
{
// Always call Close when done reading.
reader.Close();
OnDataFound(); // raise event 3
}
ale to rozwiazuje chyba tylko wywolanie dobrego exception.