504 lines
16 KiB
C++
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();
|
|
}
|
|
};
|
|
}
|
|
|