2024-03-19 17:45:12 +08:00

504 lines
16 KiB
C++

/*=============================================================================|
| PROJECT SNAP7 1.3.0 |
|==============================================================================|
| Copyright (C) 2013, 2015 Davide Nardella |
| All rights reserved. |
|==============================================================================|
| SNAP7 is free software: you can redistribute it and/or modify |
| it under the terms of the Lesser GNU General Public License as published by |
| the Free Software Foundation, either version 3 of the License, or |
| (at your option) any later version. |
| |
| It means that you can distribute your commercial software linked with |
| SNAP7 without the requirement to distribute the source code of your |
| application and without the requirement that your application be itself |
| distributed under LGPL. |
| |
| SNAP7 is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| Lesser GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License and a |
| copy of Lesser GNU General Public License along with Snap7. |
| If not, see http://www.gnu.org/licenses/ |
|=============================================================================*/
#include "s7_client.h"
//---------------------------------------------------------------------------
TSnap7Client::TSnap7Client()
{
FThread = 0;
CliCompletion = 0;
EvtJob = NULL;
EvtComplete = NULL;
FThread=NULL;
ThreadCreated = false;
}
//---------------------------------------------------------------------------
TSnap7Client::~TSnap7Client()
{
Destroying=true;
Disconnect();
CliCompletion=NULL;
if (ThreadCreated)
{
CloseThread();
delete EvtComplete;
delete EvtJob;
ThreadCreated=false;
}
}
//---------------------------------------------------------------------------
void TSnap7Client::CloseThread()
{
int Timeout;
if (FThread)
{
FThread->Terminate();
if (Job.Pending)
Timeout=3000;
else
Timeout=1000;
EvtJob->Set();
if (FThread->WaitFor(Timeout)!=WAIT_OBJECT_0)
FThread->Kill();
try {
delete FThread;
}
catch (...){
}
FThread=0;
}
}
//---------------------------------------------------------------------------
void TSnap7Client::OpenThread()
{
FThread = new TClientThread(this);
FThread->Start();
}
//---------------------------------------------------------------------------
int TSnap7Client::Reset(bool DoReconnect)
{
bool WasConnected = Connected;
if (ThreadCreated)
{
CloseThread();
Disconnect();
OpenThread();
}
else
Disconnect();
if (DoReconnect || WasConnected)
return Connect();
else
return 0;
}
//---------------------------------------------------------------------------
void TSnap7Client::DoCompletion()
{
if ((CliCompletion!=NULL) && !Destroying)
{
try{
CliCompletion(FUsrPtr, Job.Op, Job.Result);
}catch (...)
{
}
}
}
//---------------------------------------------------------------------------
int TSnap7Client::SetAsCallback(pfn_CliCompletion pCompletion, void * usrPtr)
{
CliCompletion=pCompletion;
FUsrPtr=usrPtr;
return 0;
}
//---------------------------------------------------------------------------
int TSnap7Client::GetParam(int ParamNumber, void * pValue)
{
// Actually there are no specific client params, maybe in future...
return TSnap7MicroClient::GetParam(ParamNumber, pValue);
}
//---------------------------------------------------------------------------
int TSnap7Client::SetParam(int ParamNumber, void * pValue)
{
// Actually there are no specific client params, maybe in future...
return TSnap7MicroClient::SetParam(ParamNumber, pValue);
}
//---------------------------------------------------------------------------
bool TSnap7Client::CheckAsCompletion(int &opResult)
{
if (!Job.Pending)
opResult=Job.Result;
else
if (!Destroying)
opResult=errCliJobPending; // don't set LastError here
else
{
opResult=errCliDestroying;
return true;
}
return !Job.Pending;
}
//---------------------------------------------------------------------------
int TSnap7Client::AsReadArea(int Area, int DBNumber, int Start, int Amount, int WordLen, void * pUsrData)
{
if (!Job.Pending)
{
Job.Pending = true;
Job.Op = s7opReadArea;
Job.Area = Area;
Job.Number = DBNumber;
Job.Start = Start;
Job.Amount = Amount;
Job.WordLen = WordLen;
Job.pData = pUsrData;
JobStart = SysGetTick();
StartAsyncJob();
return 0;
}
else
return SetError(errCliJobPending);
}
//---------------------------------------------------------------------------
int TSnap7Client::AsWriteArea(int Area, int DBNumber, int Start, int Amount, int WordLen, void * pUsrData)
{
int ByteSize, TotalSize;
if (!Job.Pending)
{
Job.Pending =true;
Job.Op =s7opWriteArea;
Job.Area =Area;
Job.Number =DBNumber;
Job.Start =Start;
// Performs some check first to copy the data
ByteSize=DataSizeByte(WordLen);
TotalSize=ByteSize*Amount; // Total size in bytes
if (ByteSize==0)
return SetError(errCliInvalidWordLen);
if ((TotalSize < 1) || (TotalSize > int(sizeof(opData))))
return SetError(errCliInvalidParams);
Job.Amount =Amount;
Job.WordLen =WordLen;
// Doublebuffering
memcpy(&opData, pUsrData, TotalSize);
Job.pData =&opData;
JobStart =SysGetTick();
StartAsyncJob();
return 0;
}
else
return SetError(errCliJobPending);
}
//---------------------------------------------------------------------------
int TSnap7Client::AsListBlocksOfType(int BlockType, PS7BlocksOfType pUsrData, int & ItemsCount)
{
if (!Job.Pending)
{
Job.Pending =true;
Job.Op =s7opListBlocksOfType;
Job.Area =BlockType;
Job.pData =pUsrData;
Job.pAmount =&ItemsCount;
JobStart =SysGetTick();
StartAsyncJob();
return 0;
}
else
return SetError(errCliJobPending);
}
//---------------------------------------------------------------------------
int TSnap7Client::AsReadSZL(int ID, int Index, PS7SZL pUsrData, int & Size)
{
if (!Job.Pending)
{
Job.Pending =true;
Job.Op =s7opReadSZL;
Job.ID =ID;
Job.Index =Index;
Job.pData =pUsrData;
Job.pAmount =&Size;
Job.Amount =Size;
Job.IParam =1; // Data has to be copied into user buffer
JobStart =SysGetTick();
StartAsyncJob();
return 0;
}
else
return SetError(errCliJobPending);
}
//---------------------------------------------------------------------------
int TSnap7Client::AsReadSZLList(PS7SZLList pUsrData, int &ItemsCount)
{
if (!Job.Pending)
{
Job.Pending =true;
Job.Op =s7opReadSzlList;
Job.pData =pUsrData;
Job.pAmount =&ItemsCount;
Job.Amount =ItemsCount;
JobStart =SysGetTick();
StartAsyncJob();
return 0;
}
else
return SetError(errCliJobPending);
}
//---------------------------------------------------------------------------
int TSnap7Client::AsUpload(int BlockType, int BlockNum, void * pUsrData, int & Size)
{
if (!Job.Pending)
{
Job.Pending =true;
Job.Op =s7opUpload;
Job.Area =BlockType;
Job.pData =pUsrData;
Job.pAmount =&Size;
Job.Amount =Size;
Job.Number =BlockNum;
Job.IParam =0; // not full upload, only data
JobStart =SysGetTick();
StartAsyncJob();
return 0;
}
else
return SetError(errCliJobPending);
}
//---------------------------------------------------------------------------
int TSnap7Client::AsFullUpload(int BlockType, int BlockNum, void * pUsrData, int & Size)
{
if (!Job.Pending)
{
Job.Pending =true;
Job.Op =s7opUpload;
Job.Area =BlockType;
Job.pData =pUsrData;
Job.pAmount =&Size;
Job.Amount =Size;
Job.Number =BlockNum;
Job.IParam =1; // full upload
JobStart =SysGetTick();
StartAsyncJob();
return 0;
}
else
return SetError(errCliJobPending);
}
//---------------------------------------------------------------------------
int TSnap7Client::AsDownload(int BlockNum, void * pUsrData, int Size)
{
if (!Job.Pending)
{
// Checks the size : here we only need a size>0 to avoid problems during
// doublebuffering, the real test of the block size will be done in
// Checkblock.
if (Size<1)
return SetError(errCliInvalidBlockSize);
Job.Pending =true;
Job.Op =s7opDownload;
// Doublebuffering
memcpy(&opData, pUsrData, Size);
Job.Number =BlockNum;
Job.Amount =Size;
JobStart =SysGetTick();
StartAsyncJob();
return 0;
}
else
return SetError(errCliJobPending);
}
//---------------------------------------------------------------------------
int TSnap7Client::AsCopyRamToRom(int Timeout)
{
if (!Job.Pending)
{
Job.Pending =true;
Job.Op =s7opCopyRamToRom;
if (Timeout>0)
{
Job.IParam =Timeout;
JobStart =SysGetTick();
StartAsyncJob();
return 0;
}
else
return SetError(errCliInvalidParams);
}
else
return SetError(errCliJobPending);
}
//---------------------------------------------------------------------------
int TSnap7Client::AsCompress(int Timeout)
{
if (!Job.Pending)
{
Job.Pending =true;
Job.Op =s7opCompress;
if (Timeout>0)
{
Job.IParam =Timeout;
JobStart =SysGetTick();
StartAsyncJob();
return 0;
}
else
return SetError(errCliInvalidParams);
}
else
return SetError(errCliJobPending);
}
//---------------------------------------------------------------------------
int TSnap7Client::AsDBRead(int DBNumber, int Start, int Size, void * pUsrData)
{
return AsReadArea(S7AreaDB, DBNumber, Start, Size, S7WLByte, pUsrData);
}
//---------------------------------------------------------------------------
int TSnap7Client::AsDBWrite(int DBNumber, int Start, int Size, void * pUsrData)
{
return AsWriteArea(S7AreaDB, DBNumber, Start, Size, S7WLByte, pUsrData);
}
//---------------------------------------------------------------------------
int TSnap7Client::AsMBRead(int Start, int Size, void * pUsrData)
{
return AsReadArea(S7AreaMK, 0, Start, Size, S7WLByte, pUsrData);
}
//---------------------------------------------------------------------------
int TSnap7Client::AsMBWrite(int Start, int Size, void * pUsrData)
{
return AsWriteArea(S7AreaMK, 0, Start, Size, S7WLByte, pUsrData);
}
//---------------------------------------------------------------------------
int TSnap7Client::AsEBRead(int Start, int Size, void * pUsrData)
{
return AsReadArea(S7AreaPE, 0, Start, Size, S7WLByte, pUsrData);
}
//---------------------------------------------------------------------------
int TSnap7Client::AsEBWrite(int Start, int Size, void * pUsrData)
{
return AsWriteArea(S7AreaPE, 0, Start, Size, S7WLByte, pUsrData);
}
//---------------------------------------------------------------------------
int TSnap7Client::AsABRead(int Start, int Size, void * pUsrData)
{
return AsReadArea(S7AreaPA, 0, Start, Size, S7WLByte, pUsrData);
}
//---------------------------------------------------------------------------
int TSnap7Client::AsABWrite(int Start, int Size, void * pUsrData)
{
return AsWriteArea(S7AreaPA, 0, Start, Size, S7WLByte, pUsrData);
}
//---------------------------------------------------------------------------
int TSnap7Client::AsTMRead(int Start, int Amount, void * pUsrData)
{
return AsReadArea(S7AreaTM, 0, Start, Amount, S7WLTimer, pUsrData);
}
//---------------------------------------------------------------------------
int TSnap7Client::AsTMWrite(int Start, int Amount, void * pUsrData)
{
return AsWriteArea(S7AreaTM, 0, Start, Amount, S7WLTimer, pUsrData);
}
//---------------------------------------------------------------------------
int TSnap7Client::AsCTRead(int Start, int Amount, void * pUsrData)
{
return AsReadArea(S7AreaCT, 0, Start, Amount, S7WLCounter, pUsrData);
}
//---------------------------------------------------------------------------
int TSnap7Client::AsCTWrite(int Start, int Amount, void * pUsrData)
{
return AsWriteArea(S7AreaCT, 0, Start, Amount, S7WLCounter, pUsrData);
}
//---------------------------------------------------------------------------
int TSnap7Client::AsDBGet(int DBNumber, void * pUsrData, int &Size)
{
if (!Job.Pending)
{
if (Size<=0)
return SetError(errCliInvalidBlockSize);
Job.Pending =true;
Job.Op =s7opDBGet;
Job.Number =DBNumber;
Job.pData =pUsrData;
Job.pAmount =&Size;
Job.Amount =Size;
JobStart =SysGetTick();
StartAsyncJob();
return 0;
}
else
return SetError(errCliJobPending);
}
//---------------------------------------------------------------------------
int TSnap7Client::AsDBFill(int DBNumber, int FillChar)
{
if (!Job.Pending)
{
Job.Pending =true;
Job.Op =s7opDBFill;
Job.Number =DBNumber;
Job.IParam =FillChar;
JobStart =SysGetTick();
StartAsyncJob();
return 0;
}
else
return SetError(errCliJobPending);
}
//---------------------------------------------------------------------------
void TSnap7Client::StartAsyncJob()
{
ClrError();
if (!ThreadCreated)
{
EvtJob = new TSnapEvent(false);
EvtComplete = new TSnapEvent(false);
OpenThread();
ThreadCreated=true;
}
EvtComplete->Reset(); // reset if previously was not called WaitAsCompletion
EvtJob->Set();
}
//---------------------------------------------------------------------------
int TSnap7Client::WaitAsCompletion(unsigned long Timeout)
{
if (Job.Pending)
{
if (ThreadCreated)
{
if (EvtComplete->WaitFor(Timeout)==WAIT_OBJECT_0)
return Job.Result;
else
{
if (Destroying)
return errCliDestroying;
else
return SetError(errCliJobTimeout);
}
}
else
return SetError(errCliJobTimeout);
}
else
return Job.Result;
}
//---------------------------------------------------------------------------
void TClientThread::Execute()
{
while (!Terminated)
{
FClient->EvtJob->WaitForever();
if (!Terminated)
{
FClient->PerformOperation();
FClient->EvtComplete->Set();
// Notify the caller the end of job (if callback is set)
FClient->DoCompletion();
}
};
}