PDA

View Full Version : حرفه ای: آرایه Thread ها در Windows Service



masoode
یک شنبه 16 دی 1397, 15:47 عصر
type
TPingThread = class(TThread)
private
{ Private declarations }
protected
procedure Execute; override;
procedure Pinging;
public
mIP:string;
mId:Integer;
constructor Create(Suspend:Boolean;Id:Integer;ip:string);
destructor Destroy; override;
end;

var
PingServ: TPingServ;
TikTime:Cardinal;
MyThread:array of TPingThread;
PingInterval:Integer;

سلام
من در پروژه ای احتیاج دارم یک تعداد کامپیوتر را پینگ کنم و نتیجه را در دیتابیس ذخیره کنم. تعداد کامپیوترها ثابت نیست و لیست آی پی آنها در دیتابیس ذخیره میشود.
من برای Ping گرفتن یک thread طراحی کرده ام که موقع Create کردن IP و یک Id (که مربوط به دیتابیس است) به آن ارسال می کنم. در این ترد با یک دوره تناوب مثلا یک دقیقه کامپیوتر کلاینت را پینگ می کنم.
اگر فقط یک بار از Thread استفاده کنم مشکلی نیست. اما من باید thread را به صورت آرایه پویا تعربف کنم و در ابتدای اجرای برنامه با توجه به تعداد رکوردها در دیتابیس تعداد آن را مشخص کنم.
اتفاق عجیبی که رخ می دهد این است که بعد از چند سیکل (2 یا 3 بار) کم کم داخل thread.mIP و Thread.mId مقادیر بی معنی دیده می شود!!
آیا نباید کلاس تردها را به صورت آرایه استفاده کرد؟
ظاهرا مقادیر متغییر ها در تردها تداخل میکند!
در ضمن برنامه من Windows Service App است

masoode
یک شنبه 16 دی 1397, 16:46 عصر
این هم سورس برنامه:
unit untMain;

interface

uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.SvcMgr, Vcl.Dialogs,
Data.DB, Data.Win.ADODB, Vcl.ExtCtrls,FunUnit,System.Win.Registry,IdBaseCom ponent,ActiveX,
IdComponent, IdRawBase, IdRawClient, IdIcmpClient;

type
TPingServ = class(TService)
ADOConnection1: TADOConnection;
tmrDBConnection: TTimer;
tmrRefreshSettingFromDB: TTimer;
procedure RefreshSettingFromDB;
procedure tmrRefreshSettingFromDBTimer(Sender: TObject);
procedure tmrDBConnectionTimer(Sender: TObject);
procedure ServiceAfterInstall(Sender: TService);
procedure ServiceExecute(Sender: TService);
procedure ServiceStart(Sender: TService; var Started: Boolean);
procedure ServiceStop(Sender: TService; var Stopped: Boolean);
procedure ServiceCreate(Sender: TObject);
private

{ Private declarations }
public
procedure ReadIPList;
function GetServiceController: TServiceController; override;
{ Public declarations }
end;

type
TPingThread = class(TThread)
private
{ Private declarations }
protected
procedure Execute; override;
procedure Pinging;
public
mIP:string;
mId:Integer;
constructor Create(Suspend:Boolean;Id:Integer;ip:string);
destructor Destroy; override;
end;

var
PingServ: TPingServ;
TikTime:Cardinal;
MyThread:array of TPingThread;
PingInterval:Integer;
const
MachineId=0;

implementation

{$R *.dfm}

procedure ServiceController(CtrlCode: DWord); stdcall;
begin
PingServ.Controller(CtrlCode);
end;

function TPingServ.GetServiceController: TServiceController;
begin
Result := ServiceController;
end;

procedure TPingThread.Pinging;
var
Client:TIdIcmpClient;
ADOCon:TADOConnection;
mQuery:TADOQuery;
DBServer,UserID,Password:string;
begin
WriteLog('**'+IntToStr(mId)+' '+mIP);
Client:=TIdIcmpClient.Create(nil);
Client.Host:=mIP;
Client.ReceiveTimeout:=5000;
Client.Ping;
ADOCon:=TADOConnection.Create(nil);
ReadCryptedUserIDAndPasswordToIni(AppPath+'setting s.ini',DBServer,UserID,Password);
ADOCon.ConnectionString:='Provider=SQLOLEDB.1;Pass word='+Password+';Persist Security Info=True;User ID='+UserID+';Initial Catalog=Smart Factory;Data Source='+DBServer+';Use Procedure for Prepare=1;Auto Translate=True;Packet Size=4096;Use Encryption for Data=False;Tag with column collation when possible=False';
ADOCon.LoginPrompt:=False;
mQuery:=TADOQuery.Create(nil);
mQuery.Connection:=ADOCon;
try
ADOCon.Connected:=True;
if Client.ReplyStatus.BytesReceived <= 0 then begin
mQuery.SQL.Add('INSERT INTO tblIPPingLog(DT,Id,[Reply],[Time])VALUES(SYSDATETIME(),');
mQuery.SQL.Add(IntToStr(mId)+',0,50000)');
end else begin
mQuery.SQL.Add('INSERT INTO tblIPPingLog(DT,Id,[Reply],[Time])VALUES(SYSDATETIME(),');
mQuery.SQL.Add(IntToStr(mId)+',1,'+IntToStr(Client .ReplyStatus.MsRoundTripTime)+')');
end;
if mQuery.SQL.Count>0 then
mQuery.ExecSQL;
except
On E:Exception Do
WriteLog(e.Message,'TPingThread.Pinging');
end;
mQuery.Free;
ADOCon.Free;
Client.Free;
WriteLog('**++'+IntToStr(mId)+' '+mIP);
end;

procedure TPingServ.ReadIPList;
var
mQuery:TADOQuery;
i:Integer;
ss:string;
begin
ADOConnection1.Connected:=True;
mQuery:=TADOQuery.Create(Self);
mQuery.Connection:=ADOConnection1;
mQuery.SQL.Add('SELECT top(1) Id,IP FROM tblIPList ORDER BY Id');
mQuery.Open;
if mQuery.RecordCount>0 then begin
WriteLog(IntToStr(mQuery.RecordCount));
SetLength(MyThread,mQuery.RecordCount);
for I := 0 to mQuery.RecordCount-1 do begin
MyThread[i] := TPingThread.Create(True,mQuery.FieldByName('Id').A sInteger,mQuery.FieldByName('IP').AsString);
MyThread[i].Start;
mQuery.Next;
end;
//for I := 0 to mQuery.RecordCount-1 do
// MyThread[i].Start;
end;
mQuery.Free;
end;

procedure TPingServ.RefreshSettingFromDB;
var
mQuery:TADOQuery;
begin
if not ADOConnection1.Connected then begin
tmrDBConnection.Enabled:=True;
exit;
end;
mQuery:=TADOQuery.Create(Self);
mQuery.Connection:=ADOConnection1;
try
mQuery.Connection:=ADOConnection1;
mQuery.SQL.Text:='SELECT id,Value FROM tblSettings WHERE MachineId='+IntToStr(MachineId);
mQuery.Open;
tmrDBConnection.Interval:=VariantToInteger(mQuery. Lookup('Id','20','Value'),10)*1000;
tmrRefreshSettingFromDB.Interval:=VariantToInteger (mQuery.Lookup('Id','21','Value'),10)*1000;
PingInterval:=VariantToInteger(mQuery.Lookup('Id', '22','Value'),10)*1000;
except
WriteLog('Database Connection Error','TPingServ.RefreshSettingFromDB',True);
tmrDBConnection.Enabled:=True;
end;
mQuery.Free;
//ReadIPList;
//tmrRefreshSettingFromDB.Enabled:=True;
end;

procedure TPingServ.ServiceAfterInstall(Sender: TService);
var
Reg: TRegistry;
begin
Reg := TRegistry.Create(KEY_READ or KEY_WRITE);
try
Reg.RootKey := HKEY_LOCAL_MACHINE;
if Reg.OpenKey('\SYSTEM\CurrentControlSet\Services\' + Name, false) then
begin
Reg.WriteString('Description', 'Insert IP Ping Log to Database');
Reg.CloseKey;
end;
finally
Reg.Free;
end;
end;

procedure TPingServ.ServiceCreate(Sender: TObject);
var
mQuery:TADOQuery;
i:Integer;
begin
PingInterval:=30000;
tmrDBConnectionTimer(Self);
RefreshSettingFromDB;
end;

procedure TPingServ.ServiceExecute(Sender: TService);
var
i:Integer;
ss:string;
begin
CoInitialize(nil);
//RefreshSettingFromDB;
ReadIPList;
tmrDBConnection.Enabled:=True;
//tmrRefreshSettingFromDB.Enabled:=True;
while not Terminated do begin
sleep(1000);
ss:='!!!';
for I := Low(MyThread) to High(MyThread) do
ss:=ss+IntToStr(MyThread[i].mId)+' ';
WriteLog(ss);
//for I := Low(MyThread) to High(MyThread) do
// MyThread.Start;
ServiceThread.ProcessRequests(False);// wait for termination
end;
CoUninitialize;
end;

procedure TPingServ.ServiceStart(Sender: TService; var Started: Boolean);
begin
WriteLog('Service Started','Service',True);
TikTime:=GetTickCount;
end;

procedure TPingServ.ServiceStop(Sender: TService; var Stopped: Boolean);
begin
WriteLog('Service Stoped','Service',True);
end;

procedure TPingServ.tmrDBConnectionTimer(Sender: TObject);
var
DBServer,UserID,Password:string;
begin
tmrDBConnection.Enabled:=False;
if ADOConnection1.Connected then
ADOConnection1.Close;
ReadCryptedUserIDAndPasswordToIni(AppPath+'setting s.ini',DBServer,UserID,Password);
ADOConnection1.ConnectionString:='Provider=SQLOLED B.1;Password='+Password+';Persist Security Info=True;User ID='+UserID+';Initial Catalog=Smart Factory;Data Source='+DBServer+';Use Procedure for Prepare=1;Auto Translate=True;Packet Size=4096;Use Encryption for Data=False;Tag with column collation when possible=False';
try
ADOConnection1.Connected:=True;
except
WriteLog('Database Connection Error','TPingServ.tmrDBConnectionTimer',True);
tmrDBConnection.Enabled:=True;
end;
end;

procedure TPingServ.tmrRefreshSettingFromDBTimer(Sender: TObject);
begin
RefreshSettingFromDB;
end;

{ TPingThread }

constructor TPingThread.Create(Suspend: Boolean; Id: Integer; ip: string);
begin
inherited Create(true);
FreeOnTerminate := True;
mIP:=ip;
mId:=Id;
end;

destructor TPingThread.Destroy;
begin
inherited;
end;

procedure TPingThread.Execute;
begin
while not Terminated do begin
WriteLog('Start Pinging Id='+IntToStr(mId)+mIP);
Pinging;
WriteLog('End Pinging Id='+IntToStr(mId)+mIP+'Interval='+IntToStr(PingIn terval));
Sleep(PingInterval);
end;
end;

end.

اگر خط 116 را به صورت زیر تغییر بدهم نتایج عجیب و غریبی به دست می آید:
mQuery.SQL.Add('SELECT Id,IP FROM tblIPList ORDER BY Id');

pe32_64
یک شنبه 16 دی 1397, 21:11 عصر
type
TPingThread = class(TThread)
private
{ Private declarations }
protected
procedure Execute; override;
procedure Pinging;
public
mIP:string;
mId:Integer;
constructor Create(Suspend:Boolean;Id:Integer;ip:string);
destructor Destroy; override;
end;

var
PingServ: TPingServ;
TikTime:Cardinal;
MyThread:array of TPingThread;
PingInterval:Integer;

سلام
من در پروژه ای احتیاج دارم یک تعداد کامپیوتر را پینگ کنم و نتیجه را در دیتابیس ذخیره کنم. تعداد کامپیوترها ثابت نیست و لیست آی پی آنها در دیتابیس ذخیره میشود.
من برای Ping گرفتن یک thread طراحی کرده ام که موقع Create کردن IP و یک Id (که مربوط به دیتابیس است) به آن ارسال می کنم. در این ترد با یک دوره تناوب مثلا یک دقیقه کامپیوتر کلاینت را پینگ می کنم.
اگر فقط یک بار از Thread استفاده کنم مشکلی نیست. اما من باید thread را به صورت آرایه پویا تعربف کنم و در ابتدای اجرای برنامه با توجه به تعداد رکوردها در دیتابیس تعداد آن را مشخص کنم.
اتفاق عجیبی که رخ می دهد این است که بعد از چند سیکل (2 یا 3 بار) کم کم داخل thread.mIP و Thread.mId مقادیر بی معنی دیده می شود!!
آیا نباید کلاس تردها را به صورت آرایه استفاده کرد؟
ظاهرا مقادیر متغییر ها در تردها تداخل میکند!
در ضمن برنامه من Windows Service App است
Thread synchronization رو یک جستجو بکنید .
در ثانی کدتون رو با Parallel Programming Library پیاده سازی کنید.

masoode
دوشنبه 17 دی 1397, 09:30 صبح
Thread synchronization رو یک جستجو بکنید .
در ثانی کدتون رو با Parallel Programming Library پیاده سازی کنید.

یعنی به نظر شما با یک دستکاری یا تغییر جزئی کارم راه نمی افته؟
آخه برای یک ترد هیچ مشکلی نداره اما وقتی 2 تا یا بیشتر می شه انگار فضای اختصاص داده شده حافظه قاطی پاطی می شه!

Mahmood_M
دوشنبه 17 دی 1397, 12:37 عصر
منظورتون از "مقادیر بی معنی" چیه ؟ نمونه قرار بدید
چند نکته در مورد کد :
زمانی که یک شی رو Create می کنید بهتره از فرمت زیر استفاده کنید تا مطمئن باشید که اون شی حتما آزاد میشه :


mQuery := TADOQuery.Create(nil);
try
try
...
except
...
end;
finally
mQuery.Free;
end;

در حال حاضر اگر در بین کار خطایی رخ بده حتما Memory-Leak ایجاد میشه و برای برنامه ای که به صورت Service و دایما در حال اجراست وجود Memory-Leak حتما مشکل ساز میشه
برای Object هایی که در Thread استفاده می کنید بهتره که اونها رو در Constructor مربوط به Thread بسازید و در Destructor آزاد کنید
نیاز نیست هر بار که حلقه داخل Thread اجرا میشه دوباره Connect بشید به بانک اطلاعاتی، اگر در Constructor مربوط به Thread اشیا Connection و Query و IdTCPClient رو بسازید ، می تونید در ابتدای متد Execute به بانک اطلاعاتی وصل بشید و در داخل حلقه فقط چک کنید اگر ارتباط قطع شده بود دوباره متصل بشید
قبل از حلقه ای که برای ساخت Thread نوشتید از دستور mQuery.First استفاده کنید تا اطمینان داشته باشید که حلقه از رکورد اول شروع میشه، در حالت کلی برای پیمایش یک DataSet بهتره که از حلقه While و بررسی TDataSet.Eof استفاده بشه
...
چند نمونه از خروجی برنامه بذارید تا بررسی بشه

masoode
دوشنبه 17 دی 1397, 13:41 عصر
منظورتون از "مقادیر بی معنی" چیه ؟ نمونه قرار بدید
چند نکته در مورد کد :
زمانی که یک شی رو Create می کنید بهتره از فرمت زیر استفاده کنید تا مطمئن باشید که اون شی حتما آزاد میشه :


mQuery := TADOQuery.Create(nil);
try
try
...
except
...
end;
finally
mQuery.Free;
end;

در حال حاضر اگر در بین کار خطایی رخ بده حتما Memory-Leak ایجاد میشه و برای برنامه ای که به صورت Service و دایما در حال اجراست وجود Memory-Leak حتما مشکل ساز میشه
برای Object هایی که در Thread استفاده می کنید بهتره که اونها رو در Constructor مربوط به Thread بسازید و در Destructor آزاد کنید
نیاز نیست هر بار که حلقه داخل Thread اجرا میشه دوباره Connect بشید به بانک اطلاعاتی، اگر در Constructor مربوط به Thread اشیا Connection و Query و IdTCPClient رو بسازید ، می تونید در ابتدای متد Execute به بانک اطلاعاتی وصل بشید و در داخل حلقه فقط چک کنید اگر ارتباط قطع شده بود دوباره متصل بشید
قبل از حلقه ای که برای ساخت Thread نوشتید از دستور mQuery.First استفاده کنید تا اطمینان داشته باشید که حلقه از رکورد اول شروع میشه، در حالت کلی برای پیمایش یک DataSet بهتره که از حلقه While و بررسی TDataSet.Eof استفاده بشه
...
چند نمونه از خروجی برنامه بذارید تا بررسی بشه

ممنون از وقتی گذاشتید. مواردی فرمودید را حتماً برای بهینه شدن اعمال خواهم کرد اما فکر نکنم مشکلم با آنها حل بشه!

ss:='!!!';
for I := Low(MyThread) to High(MyThread) do
ss:=ss+IntToStr(MyThread[i].mId)+' ';
WriteLog(ss);

در سطر 197 تا 200 هر یک ثانیه یک بار وضعیت متغیر MyThread[i].mId (و MyThread[i].mIP که اینجا پاکش کرده ام) را در یک فایل log میگیرم. من برای تست برنامه در دیتابیس 8 آدرس آی پی ثبت کرده ام که آی دی های آنها فعلا از 1 تا 8 است. نتیجه فایل log در حالتی که فقط یک ip را میخواند مشکلی ندارد و همیشه می نویسد

1397/10/16 17:09 !!!1
1397/10/16 17:09 !!!1
1397/10/16 17:09 !!!1
1397/10/16 17:09 !!!1
1397/10/16 17:09 !!!1
1397/10/16 17:09 !!!1
1397/10/16 17:09 !!!1
1397/10/16 17:09 !!!1
1397/10/16 17:09 !!!1
1397/10/16 17:09 !!!1
1397/10/16 17:09 !!!1
1397/10/16 17:09 !!!1
1397/10/16 17:09 !!!1
1397/10/16 17:09 !!!1
1397/10/16 17:09 !!!1

اما وقتی 8 تایی میشود نتایج متفاوت اما معمولا این جوری میشود:

1397/10/16 15:53 !!!1 2 3 4 5 6 7 8
1397/10/16 15:54 !!!3145778 2 3 4 5 6 7 0
1397/10/16 15:54 !!!3145778 2 3 4 5 6 7 0
1397/10/16 15:54 !!!3145778 2 3 7929977 46 6 7 0
1397/10/16 15:54 !!!3145778 2 3 7929977 46 6 7 0
1397/10/16 15:54 !!!3145778 2 3 7929977 46 6 7 0
1397/10/16 15:54 !!!3145778 2 3 7929977 46 6 7 0
1397/10/16 15:54 !!!3145778 2 3 7929977 46 6 7 0
1397/10/16 15:54 !!!3145778 2 3 7929977 46 6 7 0
1397/10/16 15:54 !!!3145778 2 3 7929977 46 6 7 0
1397/10/16 15:54 !!!3145778 2 3 7929977 46 6 7 0
1397/10/16 15:54 !!!3145778 2 3 7929977 46 6 7 0
1397/10/16 15:54 !!!3145778 2 3 7929977 46 6 7 0

Mahmood_M
دوشنبه 17 دی 1397, 20:20 عصر
عرض کردم که برای حل مشکل باید نمونه خروجی رو قرار بدید، مواردی که گفته شد برای جلوگیری از مشکلات دیگه ست
سورس دستور WriteLog رو قرار بدید، احتمالا مشکل در این دستور ایجاد میشه
از این دستور در چند ترد به صورت همزمان استفاده کردید، بنابراین باید Synchronization رو رعایت کنید، مثلا باید از CriticalSection استفاده کنید
راه درست برای نوشتن Log اینه که یک صف ( مثلا یک StringList ) از خطوطی که باید در لاگ نوشته بشه داشته باشید و هر ترد فقط متن مورد نظرش رو در داخل این صف قرار بده و در یک ترد جداگانه این لیست پیمایش بشه و در فایل Log نوشته بشه. برای ثبت متن Log از درون هر ترد باز باید از CriticalSection استفاده کنید یا اینکه از کلاسهای Thread-Safe مثل TThreadList استفاده کنید که مخصوص همین کار ایجاد شدن و به صورت داخلی Synchronization رو مدیریت می کنن

masoode
سه شنبه 18 دی 1397, 08:58 صبح
عرض کردم که برای حل مشکل باید نمونه خروجی رو قرار بدید، مواردی که گفته شد برای جلوگیری از مشکلات دیگه ست
سورس دستور WriteLog رو قرار بدید، احتمالا مشکل در این دستور ایجاد میشه
از این دستور در چند ترد به صورت همزمان استفاده کردید، بنابراین باید Synchronization رو رعایت کنید، مثلا باید از CriticalSection استفاده کنید
راه درست برای نوشتن Log اینه که یک صف ( مثلا یک StringList ) از خطوطی که باید در لاگ نوشته بشه داشته باشید و هر ترد فقط متن مورد نظرش رو در داخل این صف قرار بده و در یک ترد جداگانه این لیست پیمایش بشه و در فایل Log نوشته بشه. برای ثبت متن Log از درون هر ترد باز باید از CriticalSection استفاده کنید یا اینکه از کلاسهای Thread-Safe مثل TThreadList استفاده کنید که مخصوص همین کار ایجاد شدن و به صورت داخلی Synchronization رو مدیریت می کنن

کد writelog را خیلی وقت پیش نوشته ام و توی یک Unit دیگر است این هم کدش:

procedure WriteLog(eDesc:string; eLocation: String=''; UseMiladiDate:boolean=False);
var
Y,M,D:Word;
Dir,tFileName,LineL:String;
tFile:TextFile;
begin
if not UseMiladiDate then
DecodeDateShamsi(Y,M,D)
else
DecodeDate(Now,Y,M,D);
Dir:=AppPath+FIX_STR_LEN(IntToStr(Y),4)+'\';
if not DirectoryExists(Dir) then CreateDir(Dir);
Dir:=Dir+FIX_STR_LEN(IntToStr(M),2)+'\';
if not DirectoryExists(Dir) then CreateDir(Dir);
tFileName:=Dir+FIX_STR_LEN(IntToStr(D),2)+'.log';
AssignFile(tFile,tFileName);
if not FileExists(tFileName) then
Rewrite(tFile)
else
Append(tFile);
if eLocation<>'' then
LineL:=FIX_STR_LEN(IntToStr(Y),4)+'/'+FIX_STR_LEN(IntToStr(M),2)+'/'+FIX_STR_LEN(IntToStr(D),2)+' '+FormatDateTime('HH:MM',Time)+' '+eDesc+' in '+eLocation
else
LineL:= FIX_STR_LEN(IntToStr(Y),4)+'/'+FIX_STR_LEN(IntToStr(M),2)+'/'+FIX_STR_LEN(IntToStr(D),2)+' '+FormatDateTime('HH:MM',Time)+' '+eDesc;
Writeln(tFile,LineL);
CloseFile(tFile);
end;

به نظر شما اگر اشکال از Log باشه، از CodeSite هم استفاده کنم ممکنه این مشکل پیش بیاد؟

masoode
سه شنبه 18 دی 1397, 10:04 صبح
با راهنمایی شما امروز یک چیز جدید یاد گرفتم :بوس::بوس::بوس:
عجب چیزیه این CriticalSection
جالبه قبلا خود شما برای من توضیح داده بودید اما من فراموش کرده بودم
http://barnamenevis.org/showthread.php?522544-%D8%B3%D8%A7%D8%AE%D8%AA%D9%86-log-%D8%AF%D8%B1-thread&highlight=CriticalSection
فعلا برای اینکه کارم راه بیوفته لاگ گرفتن را حدف کردم. مشکلم حل شد.
از CodeSite قبلا خیلی کم برای تست استفاده کرده ام و یک کم بلدم باهاش کار کنم این هم لینک آموزشش:
http://www.drbob42.com/examines/examinD2.htm
البته قبلا یک آموزش فارسی کامل دیده بودم که پیداش نکردم.
حالا مشکل اینجاست که دستورات CodeSite را که در ترد استفاده میکنم کار نمی کند!
procedure TPingThread.Pinging;
var
Client:TIdIcmpClient;
ADOCon:TADOConnection;
mQuery:TADOQuery;
DBServer,UserID,Password:string;
begin
CodeSite.EnterMethod('TPingThread.Pinging');
CodeSite.Send('Id=',mid);
CodeSite.Send('IP=',mIP);
//WriteLog('**'+IntToStr(mId)+' '+mIP);
Client:=TIdIcmpClient.Create(nil);
Client.Host:=mIP;
Client.ReceiveTimeout:=5000;
Client.Ping;
ADOCon:=TADOConnection.Create(nil);
ReadCryptedUserIDAndPasswordToIni(AppPath+'setting s.ini',DBServer,UserID,Password);
ADOCon.ConnectionString:='Provider=SQLOLEDB.1;Pass word='+Password+';Persist Security Info=True;User ID='+UserID+';Initial Catalog=Smart Factory;Data Source='+DBServer+';Use Procedure for Prepare=1;Auto Translate=True;Packet Size=4096;Use Encryption for Data=False;Tag with column collation when possible=False';
ADOCon.LoginPrompt:=False;
mQuery:=TADOQuery.Create(nil);
mQuery.Connection:=ADOCon;
try
ADOCon.Connected:=True;
if Client.ReplyStatus.BytesReceived <= 0 then begin
mQuery.SQL.Add('INSERT INTO tblIPPingLog(DT,Id,[Reply],[Time])VALUES(SYSDATETIME(),');
mQuery.SQL.Add(IntToStr(mId)+',0,50000)');

end else begin
mQuery.SQL.Add('INSERT INTO tblIPPingLog(DT,Id,[Reply],[Time])VALUES(SYSDATETIME(),');
mQuery.SQL.Add(IntToStr(mId)+',1,'+IntToStr(Client .ReplyStatus.MsRoundTripTime)+')');
end;
CodeSite.Send(mQuery.SQL.Text);
if mQuery.SQL.Count>0 then
mQuery.ExecSQL;
except
//On E:Exception Do
// WriteLog(e.Message,'TPingThread.Pinging');
end;
mQuery.Free;
ADOCon.Free;
Client.Free;
//WriteLog('**++'+IntToStr(mId)+' '+mIP);
CodeSite.ExitMethod('TPingThread.Pinging');