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

1179 lines
32 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_partner.h"
//------------------------------------------------------------------------------
static PServersManager ServersManager = NULL;
//------------------------------------------------------------------------------
int ServersManager_GetServer(longword BindAddress, PConnectionServer &Server)
{
if (ServersManager == NULL)
{
ServersManager = new TServersManager();
}
return ServersManager->GetServer(BindAddress, Server);
}
//------------------------------------------------------------------------------
void ServersManager_RemovePartner(PConnectionServer Server, PSnap7Partner Partner)
{
if (ServersManager != NULL)
{
ServersManager->RemovePartner(Server, Partner);
if (ServersManager->ServersCount==0)
{
delete ServersManager;
ServersManager = NULL;
}
}
}
//------------------------------------------------------------------------------
// CONNECTION SERVERS MANAGER
//------------------------------------------------------------------------------
TServersManager::TServersManager()
{
cs = new TSnapCriticalSection;
memset(Servers,0,sizeof(Servers));
ServersCount=0;
}
//---------------------------------------------------------------------------
TServersManager::~TServersManager()
{
int c;
Lock();
if (ServersCount>0)
{
for (c = 0; c < MaxAdapters; c++)
{
if (Servers[c]!=0)
{
delete Servers[c];
Servers[c]=0;
ServersCount--;
}
}
}
Unlock();
delete cs;
}
//---------------------------------------------------------------------------
void TServersManager::Lock()
{
cs->Enter();
}
//---------------------------------------------------------------------------
void TServersManager::Unlock()
{
cs->Leave();
}
//---------------------------------------------------------------------------
void TServersManager::AddServer(PConnectionServer Server)
{
int c;
Lock();
for (c = 0; c < MaxAdapters; c++)
{
if (Servers[c]==0)
{
Servers[c]=Server;
ServersCount++;
break;
}
}
Unlock();
}
//---------------------------------------------------------------------------
int TServersManager::CreateServer(longword BindAddress, PConnectionServer &Server)
{
in_addr sin;
sin.s_addr=BindAddress;
int Result;
if (ServersCount<MaxAdapters)
{
Server = new TConnectionServer();
Result=Server->StartTo(inet_ntoa(sin));
if (Result!=0)
{
delete Server;
Server=0;
return Result;
}
AddServer(Server);
return 0;
}
else
return errServerNoRoom;
}
//---------------------------------------------------------------------------
int TServersManager::GetServer(longword BindAddress, PConnectionServer &Server)
{
int c;
Server=0;
for (c = 0; c < ServersCount; c++)
{
if (Servers[c]->LocalBind==BindAddress)
{
Server=Servers[c];
break;
}
}
if (Server==0)
return CreateServer(BindAddress, Server);
else
return 0;
}
//---------------------------------------------------------------------------
void TServersManager::RemovePartner(PConnectionServer Server, PSnap7Partner Partner)
{
int c;
Server->RemovePartner(Partner);
if (Server->PartnersCount==0)
{
Lock();
for (c = 0; c < MaxAdapters; c++)
{
if (Servers[c]==Server)
{
Servers[c]=0;
ServersCount--;
break;
}
}
Unlock();
delete Server;
}
}
//---------------------------------------------------------------------------
// CONNECTION SERVER
//------------------------------------------------------------------------------
void TConnListenerThread::Execute()
{
socket_t Sock;
bool Valid;
while (!Terminated)
{
if (FListener->CanRead(FListener->WorkInterval))
{
Sock=FListener->SckAccept(); // in any case we must accept
Valid=Sock!=INVALID_SOCKET;
// check if we are not destroying
if ((!Terminated) && (!FServer->Destroying))
{
if (Valid)
FServer->Incoming(Sock);
}
else
if (Valid)
Msg_CloseSocket(Sock);
};
}
}
//------------------------------------------------------------------------------
TConnectionServer::TConnectionServer()
{
cs = new TSnapCriticalSection;
memset(Partners,0,sizeof(Partners));
FRunning = false;
PartnersCount = 0;
}
//------------------------------------------------------------------------------
TConnectionServer::~TConnectionServer()
{
Stop();
delete cs;
}
//---------------------------------------------------------------------------
void TConnectionServer::Lock()
{
cs->Enter();
}
void TConnectionServer::Unlock()
{
cs->Leave();
}
//---------------------------------------------------------------------------
int TConnectionServer::Start()
{
int Result;
// Creates the listener
SockListener = new TMsgSocket();
strncpy(SockListener->LocalAddress,FLocalAddress,16);
SockListener->LocalPort=isoTcpPort;
// Binds
Result=SockListener->SckBind();
if (Result==0)
{
LocalBind=SockListener->LocalBind;
// Listen
Result=SockListener->SckListen();
if (Result==0)
{
// Creates the Listener thread
ServerThread = new TConnListenerThread(SockListener, this);
ServerThread->Start();
}
else
delete SockListener;
}
else
delete SockListener;
FRunning=Result==0;
return Result;
}
//---------------------------------------------------------------------------
int TConnectionServer::StartTo(const char *Address)
{
strncpy(FLocalAddress,Address,16);
return Start();
}
//---------------------------------------------------------------------------
void TConnectionServer::Stop()
{
if (FRunning)
{
// Kills the listener thread
ServerThread->Terminate();
if (ServerThread->WaitFor(csTimeout)!=WAIT_OBJECT_0)
ServerThread->Kill();
delete ServerThread;
// Kills the listener
delete SockListener;
FRunning = false;
}
}
//---------------------------------------------------------------------------
PSnap7Partner TConnectionServer::FindPartner(longword Address)
{
int c;
PSnap7Partner Result;
for (c = 0; c < MaxPartners; c++)
{
Result=Partners[c];
if (Result!=NULL)
{
if (Result->PeerAddress==Address)
return Result;
}
}
return NULL;
}
//------------------------------------------------------------------------------
int TConnectionServer::FirstFree()
{
int i;
for (i = 0; i < MaxPartners; i++)
{
if (Partners[i]==0)
return i;
}
return -1;
}
//------------------------------------------------------------------------------
int TConnectionServer::RegisterPartner(PSnap7Partner Partner)
{
PSnap7Partner aPartner;
int idx;
// check if already exists a passive partner linked to the same peer address
aPartner=FindPartner(Partner->PeerAddress);
if (aPartner==NULL)
{
Lock();
idx=FirstFree();
if (idx>=0)
{
Partners[idx]=Partner;
PartnersCount++;
}
Unlock();
if (idx>=0)
return 0;
else
return errParNoRoom;
}
else
return errParAddressInUse;
}
//------------------------------------------------------------------------------
void TConnectionServer::RemovePartner(PSnap7Partner Partner)
{
int c;
Lock();
for (c = 0; c < MaxPartners; c++)
{
if (Partners[c]==Partner)
{
Partners[c]=0;
PartnersCount--;
break;
}
}
Unlock();
}
//------------------------------------------------------------------------------
void TConnectionServer::Incoming(socket_t Sock)
{
longword Address;
PSnap7Partner Partner;
Address=Msg_GetSockAddr(Sock);
// Looks for a partner that is waiting for a connection from this address
Lock();
Partner=FindPartner(Address);
Unlock();
// if partner exists must not be already connected : a partner can be connected
// with only one peer at time
if ((Partner!=NULL) && (!Partner->Stopping) && (!Partner->Connected))
Partner->SetSocket(Sock);
else
Msg_CloseSocket(Sock); // we are not interested
}
//------------------------------------------------------------------------------
// PARTHER THREAD
//------------------------------------------------------------------------------
void TPartnerThread::Execute()
{
longword TheTime;
FKaElapsed=SysGetTick();
while ((!Terminated) && (!FPartner->Destroying))
{
// Check connection
while (!Terminated && !FPartner->Connected && !FPartner->Destroying)
{
if (!FPartner->ConnectToPeer())
SysSleep(FRecoveryTime);
}
// Execution
if ((!Terminated) && (!FPartner->Destroying) && (!FPartner->Execute()))
SysSleep(FRecoveryTime);
// Keep Alive
if (!Terminated && (!FPartner->Destroying) && FPartner->Active && FPartner->Connected)
{
TheTime=SysGetTick();
if (TheTime-FKaElapsed>FPartner->KeepAliveTime)
{
FKaElapsed=TheTime;
if (!FPartner->Ping(FPartner->RemoteAddress))
FPartner->Disconnect();
};
};
};
}
//------------------------------------------------------------------------------
// S7 PARTNER
//------------------------------------------------------------------------------
TSnap7Partner::TSnap7Partner(bool CreateActive)
{
// We skip RFC/ISO header, our PDU is the ISO payload
PDUH_in=PS7ReqHeader(&PDU.Payload);
FWorkerThread=0;
OnBRecv = 0;
OnBSend = 0;
Active=CreateActive;
SendEvt = new TSnapEvent(true);
RecvEvt = new TSnapEvent(true);
FSendPending = false;
FRecvPending = false;
memset(&FRecvStatus,0,sizeof(TRecvStatus));
memset(&FRecvLast,0,sizeof(TRecvLast));
FSendElapsed = 0;
Destroying = false;
// public
Linked =false;
Running =false;
BindError =false;
BRecvTimeout =3000;
BSendTimeout =3000;
RecoveryTime =500;
KeepAliveTime =5000;
NextByte =0;
PeerAddress =0;
SendTime =0;
RecvTime =0;
BytesSent =0;
BytesRecv =0;
SendErrors =0;
RecvErrors =0;
}
//------------------------------------------------------------------------------
TSnap7Partner::~TSnap7Partner()
{
Stop();
OnBRecv = 0;
OnBSend = 0;
delete SendEvt;
delete RecvEvt;
}
//------------------------------------------------------------------------------
byte TSnap7Partner::GetNextByte()
{
NextByte++;
if (NextByte==0xFF)
NextByte=1;
return NextByte;
}
//------------------------------------------------------------------------------
int TSnap7Partner::Start()
{
int Result;
PeerAddress=inet_addr(RemoteAddress);
SrcAddress =inet_addr(LocalAddress);
if (!Running)
{
if (!Active)
{
Result=ServersManager_GetServer(SrcAddress,FServer);
if (Result==0)
FServer->RegisterPartner(this);
BindError=Result!=0;
}
else
{
Linked=PeerConnect()==0;
Result=0; // we need to create the worker thread even tough it's not linked
};
// if ok create the worker thread
if (Result==0)
{
FWorkerThread = new TPartnerThread(this, RecoveryTime);
FWorkerThread->Start();
}
}
else
Result=0;
Running=Result==0;
return Result;
}
//------------------------------------------------------------------------------
int TSnap7Partner::StartTo(const char *LocAddress, const char *RemAddress, word LocTsap, word RemTsap)
{
SrcTSap=LocTsap;
DstTSap=RemTsap;
strcpy(LocalAddress,LocAddress);
strcpy(RemoteAddress,RemAddress);
return Start();
}
//------------------------------------------------------------------------------
int TSnap7Partner::Stop()
{
if (Running)
{
Stopping=true; // to prevent incoming connections
CloseWorker();
if (!Active && (FServer!=0))
ServersManager_RemovePartner(FServer, this);
if (Connected)
Disconnect();
Running =false;
Stopping=false;
};
BindError=false;
return 0;
}
//------------------------------------------------------------------------------
void TSnap7Partner::Disconnect()
{
PeerDisconnect();
Linked=false;
}
//------------------------------------------------------------------------------
int TSnap7Partner::GetParam(int ParamNumber, void * pValue)
{
switch (ParamNumber)
{
case p_u16_LocalPort:
*Puint16_t(pValue)=LocalPort;
break;
case p_u16_RemotePort:
*Puint16_t(pValue)=RemotePort;
break;
case p_i32_PingTimeout:
*Pint32_t(pValue)=PingTimeout;
break;
case p_i32_SendTimeout:
*Pint32_t(pValue)=SendTimeout;
break;
case p_i32_RecvTimeout:
*Pint32_t(pValue)=RecvTimeout;
break;
case p_i32_WorkInterval:
*Pint32_t(pValue)=WorkInterval;
break;
case p_u16_SrcRef:
*Puint16_t(pValue)=SrcRef;
break;
case p_u16_DstRef:
*Puint16_t(pValue)=DstRef;
break;
case p_u16_SrcTSap:
*Puint16_t(pValue)=SrcTSap;
break;
case p_i32_PDURequest:
*Pint32_t(pValue)=PDURequest;
break;
case p_i32_BSendTimeout:
*Pint32_t(pValue)=BSendTimeout;
break;
case p_i32_BRecvTimeout:
*Pint32_t(pValue)=BRecvTimeout;
break;
case p_u32_RecoveryTime:
*Puint32_t(pValue)=RecoveryTime;
break;
case p_u32_KeepAliveTime:
*Puint32_t(pValue)=KeepAliveTime;
break;
default: return errParInvalidParamNumber;
}
return 0;
}
//------------------------------------------------------------------------------
int TSnap7Partner::SetParam(int ParamNumber, void * pValue)
{
switch (ParamNumber)
{
case p_u16_RemotePort:
if (!Connected && Active)
RemotePort=*Puint16_t(pValue);
else
return errParCannotChangeParam;
break;
case p_i32_PingTimeout:
PingTimeout=*Pint32_t(pValue);
break;
case p_i32_SendTimeout:
SendTimeout=*Pint32_t(pValue);
break;
case p_i32_RecvTimeout:
RecvTimeout=*Pint32_t(pValue);
break;
case p_i32_WorkInterval:
WorkInterval=*Pint32_t(pValue);
break;
case p_u16_SrcRef:
SrcRef=*Puint16_t(pValue);
break;
case p_u16_DstRef:
DstRef=*Puint16_t(pValue);
break;
case p_u16_SrcTSap:
SrcTSap=*Puint16_t(pValue);
break;
case p_i32_PDURequest:
PDURequest=*Pint32_t(pValue);
break;
case p_i32_BSendTimeout:
BSendTimeout=*Pint32_t(pValue);
break;
case p_i32_BRecvTimeout:
BRecvTimeout=*Pint32_t(pValue);
break;
case p_u32_RecoveryTime:
RecoveryTime=*Puint32_t(pValue);
break;
case p_u32_KeepAliveTime:
KeepAliveTime=*Puint32_t(pValue);
break;
default: return errParInvalidParamNumber;
}
return 0;
}
//------------------------------------------------------------------------------
void TSnap7Partner::ClearRecv()
{
memset(&FRecvStatus,0,sizeof(TRecvStatus));
FRecvPending=false;
}
//------------------------------------------------------------------------------
bool TSnap7Partner::ConnectToPeer()
{
bool Result;
if (Active)
{
Result=PeerConnect()==0; // try to Connect
Linked=Result;
}
else
Result =false; // nothing : we are waiting for a connection
return Result;
}
//------------------------------------------------------------------------------
bool TSnap7Partner::PerformFunctionNegotiate()
{
PReqFunNegotiateParams ReqParams;
PResFunNegotiateParams ResParams;
TS7Answer23 Answer;
int Size;
// Setup pointers
ReqParams=PReqFunNegotiateParams(pbyte(PDUH_in)+sizeof(TS7ReqHeader));
ResParams=PResFunNegotiateParams(pbyte(&Answer)+sizeof(TS7ResHeader23));
// We are here only because we found a PduType_request, the partner can only
// handle Bs} requests and pdu negotiation requests.
// So, now we must check the incoming function
if (ReqParams->FunNegotiate!=pduNegotiate)
{
LastError=errParInvalidPDU;
return false;
};
// Prepares the answer
Answer.Header.P=0x32;
Answer.Header.PDUType =0x03;
Answer.Header.AB_EX =0x0000;
Answer.Header.Sequence=PDUH_in->Sequence;
Answer.Header.ParLen =SwapWord(sizeof(TResFunNegotiateParams));
Answer.Header.DataLen =0x0000;
Answer.Header.Error =0x0000;
// Params point at the } of the header
ResParams->FunNegotiate=pduNegotiate;
ResParams->Unknown=0x0;
// Checks PDU request length
if (SwapWord(ResParams->PDULength)>IsoPayload_Size)
ResParams->PDULength=SwapWord(IsoPayload_Size);
else
ResParams->PDULength=ReqParams->PDULength;
// We offer the same
ResParams->ParallelJobs_1=ReqParams->ParallelJobs_1;
ResParams->ParallelJobs_2=ReqParams->ParallelJobs_2;
// And store the value
PDULength=SwapWord(ResParams->PDULength);
// Sends the answer
Size=sizeof(TS7ResHeader23) + sizeof(TResFunNegotiateParams);
if (isoSendBuffer(&Answer, Size)!=0)
SetError(errParNegotiatingPDU);
Linked=LastError==0;
return Linked;
}
//------------------------------------------------------------------------------
void TSnap7Partner::CloseWorker()
{
int Timeout;
if (FWorkerThread)
{
FWorkerThread->Terminate();
if (FRecvPending || FSendPending)
Timeout=3000;
else
Timeout=1000;
if (FWorkerThread->WaitFor(Timeout)!=WAIT_OBJECT_0)
FWorkerThread->Kill();
try {
delete FWorkerThread;
}
catch (...){
}
FWorkerThread=0;
}
}
//------------------------------------------------------------------------------
bool TSnap7Partner::BlockSend()
{
PBSendReqParams ReqParams;
PBSendReqParams ResParams;
PBsendRequestData DataSendReq;
int TotalSize;
int SentSize;
int Slice;
int MaxSlice;
uintptr_t Offset;
pbyte Source;
bool First, Last;
byte Seq_IN;
int TxIsoSize;
pbyte Data;
pword TotalPackSize;
int DataPtrOffset;
word Extra;
ClrError();
TotalSize=TxBuffer.Size;
SentSize =TotalSize;
Offset=0;
First =true;
Seq_IN=0x00;
// With BSend we can transfer up to 32k (S7300) or 64k (S7400), but splitted
// into slice that cannot exced the PDU size negotiated (including various headers).
MaxSlice=PDULength-sizeof(TS7ReqHeader)-sizeof(TBSendParams)-sizeof(TBsendRequestData)-2;
ReqParams=PBSendReqParams(pbyte(PDUH_out)+sizeof(TS7ReqHeader));
ResParams=ReqParams; // pdu 7 is symmetrical
while ((TotalSize>0) && (LastError==0))
{
Source=pbyte(&TxBuffer.Data)+Offset;
Slice=TotalSize;
if (Slice>MaxSlice)
Slice=MaxSlice;
TotalSize-=Slice;
Offset+=Slice;
Last=TotalSize==0;
// Prepare send
DataPtrOffset=sizeof(TS7ReqHeader)+sizeof(TBSendParams);
// Header
PDUH_out->P=0x32; // Always 0x32
PDUH_out->PDUType=PduType_userdata; // 7
PDUH_out->AB_EX=0x0000; // Always 0x0000
PDUH_out->Sequence=GetNextWord(); // Autoinc
PDUH_out->ParLen=SwapWord(sizeof(TBSendParams)); // 16 bytes
ReqParams->Head[0]=0x00;
ReqParams->Head[1]=0x01;
ReqParams->Head[2]=0x12;
ReqParams->Plen =0x08; // length from here up the end of the record
ReqParams->Uk =0x12;
ReqParams->Tg =grBSend; // 0x46
ReqParams->SubFun =0x01;
ReqParams->Seq =Seq_IN;
ReqParams->Err =0x0000;
if (Last)
ReqParams->EoS =0x00;
else
ReqParams->EoS =0x01;
// Next byte is auto inc and not zero for partial sequences
// Is zero for lonely sequences.
if (First && Last)
ReqParams->IDSeq=0x00;
else
ReqParams->IDSeq=GetNextByte();
DataSendReq=PBsendRequestData(pbyte(PDUH_out)+DataPtrOffset);
if (First)
{
// in the first pdu, after data header there is the whole packet length
TotalPackSize=pword(pbyte(DataSendReq)+sizeof(TBsendRequestData));
Data=pbyte(TotalPackSize)+sizeof(word);
*TotalPackSize=SwapWord(word(TxBuffer.Size));
Extra=2; // extra bytes (total pack size indicator)
}
else
{
Data=pbyte(DataSendReq)+sizeof(TBsendRequestData);
Extra=0;
};
PDUH_out->DataLen=SwapWord(word(sizeof(TBsendRequestData))+Slice+Extra);
DataSendReq->Len =SwapWord(Slice+8+Extra);
TxIsoSize=Slice+sizeof(TS7ReqHeader)+sizeof(TBSendParams)+sizeof(TBsendRequestData)+Extra;
DataSendReq->FF =0xFF;
DataSendReq->TRSize =TS_ResOctet;
DataSendReq->DHead[0]=0x12;
DataSendReq->DHead[1]=0x06;
DataSendReq->DHead[2]=0x13;
DataSendReq->DHead[3]=0x00;
DataSendReq->R_ID =SwapDWord(TxBuffer.R_ID);
memcpy(Data, Source ,Slice);
if (isoExchangeBuffer(NULL, TxIsoSize)!=0)
SetError(errParSendingBlock);
if (LastError==0)
{
Seq_IN=ResParams->Seq;
if (SwapWord(ResParams->Err)!=0)
LastError=errParSendRefused;
}
if (First)
{
First =false;
MaxSlice+=2; // only in the first frame we have the extra info
};
};
SendTime=SysGetTick()-FSendElapsed;
if (LastError==0)
BytesSent+=SentSize;
return LastError==0;
}
//------------------------------------------------------------------------------
bool TSnap7Partner::PickData()
{
PBSendReqParams ReqParams;
PBSendReqParams ResParams;
PBSendResData ResData;
PBsendRequestData DataSendReq;
pbyte Source, Target;
pword TotalPackSize;
word Slice;
int AnswerLen;
ClrError();
// Setup pointers
ReqParams =PBSendReqParams(pbyte(PDUH_in)+sizeof(TS7ReqHeader));
ResParams =ReqParams; // pdu 7 is symmetrical
DataSendReq=PBsendRequestData(pbyte(ReqParams)+sizeof(TBSendParams));
// Checks if PDU is a BSend request
if ((PDUH_in->PDUType!=PduType_userdata) || (ReqParams->Tg!=grBSend))
{
LastError=errParInvalidPDU;
return false;
}
if (FRecvStatus.First)
{
TotalPackSize=(word*)(pbyte(DataSendReq)+sizeof(TBsendRequestData));
FRecvStatus.TotalLength=SwapWord(*TotalPackSize);
Source=pbyte(DataSendReq)+sizeof(TBsendRequestData)+2;
FRecvStatus.In_R_ID=SwapDWord(DataSendReq->R_ID);
FRecvStatus.Offset=0;
Slice=SwapWord(DataSendReq->Len)-10;
}
else {
Slice=SwapWord(DataSendReq->Len)-8;
Source=pbyte(DataSendReq)+sizeof(TBsendRequestData);
}
FRecvStatus.Done=ReqParams->EoS==0x00;
Target=pbyte(&RxBuffer)+FRecvStatus.Offset;
memcpy(Target, Source, Slice);
FRecvStatus.Offset+=Slice;
ResData =PBSendResData(pbyte(ResParams)+sizeof(TBSendParams));
// Send Answer
PDUH_out->ParLen =SwapWord(sizeof(TBSendParams));
PDUH_out->DataLen =SwapWord(sizeof(TBSendResData));
ResParams->Head[0]=0x00;
ResParams->Head[1]=0x01;
ResParams->Head[2]=0x12;
ResParams->Plen =0x08; // length from here up the end of the record
ResParams->Uk =0x12;
ResParams->Tg =0x86;
ResParams->SubFun =0x01;
ResParams->Seq =FRecvStatus.Seq_Out;
ResParams->Err =0x0000;
ResParams->EoS =0x00;
ResParams->IDSeq =0x00;
ResData->DHead[0] =0x0A;
ResData->DHead[1] =0x00;
ResData->DHead[2] =0x00;
ResData->DHead[3] =0x00;
AnswerLen=sizeof(TS7ReqHeader)+sizeof(TBSendParams)+sizeof(TBSendResData);
if (isoSendBuffer(NULL,AnswerLen)!=0)
SetError(errParRecvingBlock);
return LastError==0;
}
//------------------------------------------------------------------------------
bool TSnap7Partner::BlockRecv()
{
bool Result;
if (!FRecvPending) // Start sequence
{
FRecvPending=true;
FRecvStatus.First=true;
FRecvStatus.Done =false;
FRecvStatus.Seq_Out =GetNextByte();
FRecvStatus.Elapsed =SysGetTick();
FRecvLast.Done=false;
FRecvLast.Result=0;
FRecvLast.R_ID=0;
FRecvLast.Size=0;
RecvTime =0;
FRecvLast.Count++;
if (FRecvLast.Count==0xFFFFFFFF)
FRecvLast.Count=0;
};
Result=PickData();
FRecvStatus.First=false;
if (!Result || FRecvStatus.Done)
{
FRecvLast.Result=LastError;
if (Result)
{
BytesRecv+=FRecvStatus.TotalLength;
RecvTime=SysGetTick()-FRecvStatus.Elapsed;
FRecvLast.R_ID=FRecvStatus.In_R_ID;
FRecvLast.Size=FRecvStatus.TotalLength;
};
RecvEvt->Set();
if ((OnBRecv!=NULL) && !Destroying)
OnBRecv(FRecvUsrPtr, FRecvLast.Result, FRecvLast.R_ID, &RxBuffer, FRecvLast.Size);
FRecvLast.Done=true;
ClearRecv();
};
return Result;
}
//------------------------------------------------------------------------------
bool TSnap7Partner::ConnectionConfirm()
{
if (FRecvPending)
ClearRecv();
IsoConfirmConnection(pdu_type_CC); // <- Connection confirm
return LastTcpError!=WSAECONNRESET;
}
//------------------------------------------------------------------------------
int TSnap7Partner::Status()
{
if (Running)
{
if (Linked)
{
if (FRecvPending)
return par_receiving;
else
if (FSendPending)
return par_sending;
else
return par_linked;
}
else
if (Active)
return par_connecting;
else
return par_waiting;
}
else{
if ((!Active) && BindError)
return par_binderror;
else
return par_stopped;
}
}
//------------------------------------------------------------------------------
bool TSnap7Partner::Execute()
{
TPDUKind PduKind;
bool RTimeout;
bool Result =true;
// Checks if there is something to send (and we are not receiving...)
if (FSendPending && !FRecvPending)
{
Result=BlockSend();
SendEvt->Set();
if ((OnBSend!=NULL) && (!Destroying))
OnBSend(FSendUsrPtr, LastError);
FSendPending=false;
}
if (Destroying)
return false;
// Checks if there is something to recv
if (Result && CanRead(WorkInterval))
{
// Peeks info and returns PDU Kind
isoRecvPDU(&PDU);
if (LastTcpError==0)
{
// First check valid data incoming (most likely situation)
IsoPeek(&PDU,PduKind);
if (PduKind==pkValidData)
{
if (PDUH_in->PDUType==PduType_request)
{
if (FRecvPending)
ClearRecv();
Result=PerformFunctionNegotiate();
}
else // Pdu type userdata
Result=BlockRecv();
}
else
if (PduKind==pkConnectionRequest)
Result=ConnectionConfirm();
else // nothing else
Purge();
}
else
Result=false;
};
if (LastTcpError==WSAECONNRESET)
{
Result=false;
Linked=false;
}
else
if (!Result)
Disconnect();
// Check BRecv sequence timeout
RTimeout= FRecvPending && (SysGetTick()-FRecvStatus.Elapsed>longword(BRecvTimeout));
if (RTimeout)
{
LastError=errParFrameTimeout;
RecvEvt->Set();
if ((OnBRecv!=NULL) && !Destroying)
OnBRecv(FRecvUsrPtr, LastError, 0, &RxBuffer,0);
};
if (!Result || RTimeout)
ClearRecv(); // parframetimeout
return Result;
}
//------------------------------------------------------------------------------
int TSnap7Partner::BSend(longword R_ID, void *pUsrData, int Size)
{
// The block send is managed into the worker thread.
// Sync Bsend consists of AsBSend+WaitAsCompletion
int Result=AsBSend(R_ID, pUsrData, Size);
if (Result==0)
Result=WaitAsBSendCompletion(BSendTimeout);
return Result;
}
//------------------------------------------------------------------------------
int TSnap7Partner::AsBSend(longword R_ID, void *pUsrData, int Size)
{
SendTime=0;
if (Linked)
{
if (!FSendPending)
{
memcpy(&TxBuffer.Data, pUsrData, Size);
TxBuffer.R_ID=R_ID;
TxBuffer.Size=Size;
SendEvt->Reset();
FSendPending=true;
FSendElapsed=SysGetTick();
return 0;
}
else
return errParBusy;
}
else
return SetError(errParNotLinked);
}
//------------------------------------------------------------------------------
bool TSnap7Partner::CheckAsBSendCompletion(int &opResult)
{
if (!Destroying)
{
if (!FSendPending)
opResult=LastError;
else
opResult=errParBusy;
return !FSendPending;
}
else
{
opResult=errParDestroying;
return true;
}
}
//------------------------------------------------------------------------------
int TSnap7Partner::WaitAsBSendCompletion(longword Timeout)
{
if (SendEvt->WaitFor(BSendTimeout)==WAIT_OBJECT_0)
{
if (!Destroying)
return LastError;
else
return SetError(errParDestroying);
}
else
return SetError(errParSendTimeout);
}
//------------------------------------------------------------------------------
int TSnap7Partner::SetSendCallback(pfn_ParBSendCompletion pCompletion, void *usrPtr)
{
OnBSend=pCompletion;
FSendUsrPtr=usrPtr;
return 0;
}
//------------------------------------------------------------------------------
int TSnap7Partner::BRecv(longword &R_ID, void *pData, int &Size, longword Timeout)
{
int Result=0;
if (RecvEvt->WaitFor(Timeout)==WAIT_OBJECT_0)
{
R_ID =FRecvLast.R_ID;
Size =FRecvLast.Size;
if (FRecvLast.Result==0)
{
if (pData!=NULL)
memcpy(pData, &RxBuffer, Size);
else
Result=errParInvalidParams;
}
else
Result=FRecvLast.Result;
RecvEvt->Reset();
}
else
Result=errParRecvTimeout;
return SetError(Result);
}
//------------------------------------------------------------------------------
bool TSnap7Partner::CheckAsBRecvCompletion(int &opResult, longword &R_ID,
void *pData, int &Size)
{
if (Destroying)
{
Size=0;
opResult=errParDestroying;
return true;
}
bool Result=FRecvLast.Done;
if (Result)
{
Size=FRecvLast.Size;
R_ID=FRecvLast.R_ID;
opResult=FRecvLast.Result;
if ((pData!=NULL) && (Size>0) && (opResult==0))
memcpy(pData, &RxBuffer, Size);
FRecvLast.Done=false;
}
return Result;
}
//------------------------------------------------------------------------------
int TSnap7Partner::SetRecvCallback(pfn_ParBRecvCallBack pCompletion, void *usrPtr)
{
OnBRecv=pCompletion;
FRecvUsrPtr=usrPtr;
return 0;
}
//------------------------------------------------------------------------------