2160 lines
77 KiB
C++
2160 lines
77 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_server.h"
|
|
#include "s7_firmware.h"
|
|
|
|
const byte BitMask[8] = {0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
|
|
|
|
//------------------------------------------------------------------------------
|
|
// ISO/TCP WORKER CLASS
|
|
//------------------------------------------------------------------------------
|
|
bool TIsoTcpWorker::IsoPerformCommand(int &Size)
|
|
{
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
bool TIsoTcpWorker::ExecuteSend()
|
|
{
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
bool TIsoTcpWorker::ExecuteRecv()
|
|
{
|
|
TPDUKind PduKind;
|
|
int PayloadSize;
|
|
|
|
if (CanRead(WorkInterval)) // should be Small to avoid time wait during the close
|
|
{
|
|
isoRecvPDU(&PDU);
|
|
if (LastTcpError==0)
|
|
{
|
|
IsoPeek(&PDU,PduKind);
|
|
// First check valid data incoming (most likely situation)
|
|
if (PduKind==pkValidData)
|
|
{
|
|
PayloadSize=PDUSize(&PDU)-DataHeaderSize;
|
|
return IsoPerformCommand(PayloadSize);
|
|
};
|
|
// Connection request incoming
|
|
if (PduKind==pkConnectionRequest)
|
|
{
|
|
IsoConfirmConnection(pdu_type_CC); // <- Connection confirm
|
|
return LastTcpError!=WSAECONNRESET;
|
|
};
|
|
// Disconnect request incoming (only for isotcp full complient equipment, not S7)
|
|
if (PduKind==pkDisconnectRequest)
|
|
{
|
|
IsoConfirmConnection(pdu_type_DC); // <- Disconnect confirm
|
|
return false;
|
|
};
|
|
// Empty fragment, maybe an ACK
|
|
if (PduKind==pkEmptyFragment)
|
|
{
|
|
PayloadSize=0;
|
|
return IsoPerformCommand(PayloadSize);
|
|
};
|
|
// Valid PDU format but we have to discard it
|
|
if (PduKind==pkUnrecognizedType)
|
|
{
|
|
return LastTcpError!=WSAECONNRESET;
|
|
};
|
|
// Here we have an Invalid PDU
|
|
Purge();
|
|
return true;
|
|
}
|
|
else
|
|
return LastTcpError!=WSAECONNRESET;
|
|
}
|
|
else
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
bool TIsoTcpWorker::Execute()
|
|
{
|
|
return ExecuteSend() && ExecuteRecv();
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
// S7 WORKER CLASS
|
|
//------------------------------------------------------------------------------
|
|
TS7Worker::TS7Worker()
|
|
{
|
|
// We skip RFC/ISO header, our PDU is the payload
|
|
PDUH_in =PS7ReqHeader(&PDU.Payload);
|
|
FPDULength=2048;
|
|
DBCnt =0;
|
|
LastBlk =Block_DB;
|
|
}
|
|
|
|
bool TS7Worker::ExecuteRecv()
|
|
{
|
|
WorkInterval=FServer->WorkInterval;
|
|
return TIsoTcpWorker::ExecuteRecv();
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
bool TS7Worker::CheckPDU_in(int PayloadSize)
|
|
{
|
|
// Checks the size : packet size must match with header infos
|
|
int Size=SwapWord(PDUH_in->ParLen)+SwapWord(PDUH_in->DataLen)+ReqHeaderSize;
|
|
if (Size!=PayloadSize)
|
|
return false;
|
|
// Checks PDUType : must be 1 or 7
|
|
if ((PDUH_in->PDUType!=PduType_request) &&
|
|
(PDUH_in->PDUType!=PduType_userdata))
|
|
return false;
|
|
else
|
|
return true;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
byte TS7Worker::BCD(word Value)
|
|
{
|
|
return ((Value / 10) << 4) + (Value % 10);
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
void TS7Worker::FillTime(PS7Time PTime)
|
|
{
|
|
time_t Now;
|
|
time(&Now);
|
|
struct tm *DT = localtime (&Now);
|
|
|
|
PTime->bcd_year=BCD(DT->tm_year-100);
|
|
PTime->bcd_mon =BCD(DT->tm_mon+1);
|
|
PTime->bcd_day =BCD(DT->tm_mday);
|
|
PTime->bcd_hour=BCD(DT->tm_hour);
|
|
PTime->bcd_min =BCD(DT->tm_min);
|
|
PTime->bcd_sec =BCD(DT->tm_sec);
|
|
PTime->bcd_himsec=0;
|
|
PTime->bcd_dow =BCD(DT->tm_wday);
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
void TS7Worker::DoEvent(longword Code, word RetCode, word Param1, word Param2,
|
|
word Param3, word Param4)
|
|
{
|
|
FServer->DoEvent(ClientHandle,Code,RetCode,Param1,Param2,Param3,Param4);
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
void TS7Worker::DoReadEvent(longword Code, word RetCode, word Param1, word Param2,
|
|
word Param3, word Param4)
|
|
{
|
|
FServer->DoReadEvent(ClientHandle,Code,RetCode,Param1,Param2,Param3,Param4);
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
void TS7Worker::FragmentSkipped(int Size)
|
|
{
|
|
// do nothing could be used for debug purpose
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
bool TS7Worker::IsoPerformCommand(int &Size)
|
|
{
|
|
// Checks for Ack fragment
|
|
if (Size==0)
|
|
return PerformPDUAck(Size);
|
|
// First checks PDU consistence
|
|
if (CheckPDU_in(Size))
|
|
{
|
|
switch (PDUH_in->PDUType)
|
|
{
|
|
case PduType_request : return PerformPDURequest(Size);
|
|
case PduType_userdata : return PerformPDUUsrData(Size);
|
|
}
|
|
}
|
|
else
|
|
DoEvent(evcPDUincoming, evrMalformedPDU, Size, 0, 0, 0);
|
|
return false;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
bool TS7Worker::PerformPDUAck(int &Size)
|
|
{
|
|
// here we could keep track of ack empty fragment for debug purpose
|
|
return true;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
bool TS7Worker::PerformPDURequest(int &Size)
|
|
{
|
|
pbyte P;
|
|
byte PDUFun;
|
|
bool Result = true;
|
|
|
|
// We have to store PDUfun since it will be overwritten
|
|
P = pbyte(PDUH_in)+ReqHeaderSize;
|
|
PDUFun=*P;
|
|
// Watches the function
|
|
switch (PDUFun)
|
|
{
|
|
case pduFuncRead : Result=PerformFunctionRead();
|
|
break;
|
|
case pduFuncWrite : Result=PerformFunctionWrite();
|
|
break;
|
|
case pduNegotiate : Result=PerformFunctionNegotiate();
|
|
break;
|
|
case pduStart :
|
|
case pduStop : Result=PerformFunctionControl(PDUFun);
|
|
break;
|
|
case pduStartUpload :
|
|
case pduUpload :
|
|
case pduEndUpload : Result=PerformFunctionUpload();
|
|
break;
|
|
case pduReqDownload : Result=PerformFunctionDownload();
|
|
break;
|
|
// <-- Further (custom) functions can be added here
|
|
default:
|
|
DoEvent(evcPDUincoming, evrCannotHandlePDU, Size, 0, 0, 0);
|
|
};
|
|
return Result;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
bool TS7Worker::PerformPDUUsrData(int &Size)
|
|
{
|
|
PS7ReqParams7 ReqParams;
|
|
byte Tg, SubFun;
|
|
bool Result = true;
|
|
// Set Pointer to request params
|
|
ReqParams=PS7ReqParams7(pbyte(PDUH_in)+ReqHeaderSize);
|
|
Tg=ReqParams->Tg;
|
|
SubFun=ReqParams->SubFun;
|
|
// Switch type_group
|
|
switch (Tg)
|
|
{
|
|
case grProgrammer : Result=PerformGroupProgrammer();
|
|
break;
|
|
case grCyclicData : Result=PerformGroupCyclicData();
|
|
break;
|
|
case grBlocksInfo : Result=PerformGroupBlockInfo();
|
|
break;
|
|
case grSZL : Result=PerformGroupSZL();
|
|
break;
|
|
case grPassword : Result=PerformGroupSecurity();
|
|
break;
|
|
case grClock : switch (SubFun)
|
|
{
|
|
case 0x01 : Result=PerformGetClock();
|
|
break;
|
|
case 0x02 : Result=PerformSetClock();
|
|
break;
|
|
};
|
|
break;
|
|
default:
|
|
DoEvent(evcPDUincoming, evrInvalidGroupUData, Tg, 0, 0, 0);
|
|
};
|
|
return Result;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
int TS7Worker::DataSizeByte(int WordLength)
|
|
{
|
|
switch (WordLength){
|
|
case S7WLBit : return 1; // S7 sends 1 byte per bit
|
|
case S7WLByte : return 1;
|
|
case S7WLChar : return 1;
|
|
case S7WLWord : return 2;
|
|
case S7WLDWord : return 4;
|
|
case S7WLInt : return 2;
|
|
case S7WLDInt : return 4;
|
|
case S7WLReal : return 4;
|
|
case S7WLCounter : return 2;
|
|
case S7WLTimer : return 2;
|
|
default : return 0;
|
|
}
|
|
}
|
|
//==============================================================================
|
|
// FUNCTION READ
|
|
//==============================================================================
|
|
word TS7Worker::RA_NotFound(PResFunReadItem ResItem, TEv &EV)
|
|
{
|
|
ResItem->DataLength=SwapWord(0x0004);
|
|
ResItem->ReturnCode=Code7ResItemNotAvailable;
|
|
ResItem->TransportSize=0x00;
|
|
EV.EvRetCode=evrErrAreaNotFound;
|
|
return 0;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
word TS7Worker::RA_OutOfRange(PResFunReadItem ResItem, TEv &EV)
|
|
{
|
|
ResItem->DataLength=SwapWord(0x0004);
|
|
ResItem->ReturnCode=Code7AddressOutOfRange;
|
|
ResItem->TransportSize=0x00;
|
|
EV.EvRetCode=evrErrOutOfRange;
|
|
return 0;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
word TS7Worker::RA_SizeOverPDU(PResFunReadItem ResItem, TEv &EV)
|
|
{
|
|
ResItem->DataLength=SwapWord(0x0004);
|
|
ResItem->ReturnCode=byte(SwapWord(Code7DataOverPDU));
|
|
ResItem->TransportSize=0x00;
|
|
EV.EvRetCode=evrErrOverPDU;
|
|
return 0;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
PS7Area TS7Worker::GetArea(byte S7Code, word index)
|
|
{
|
|
switch(S7Code)
|
|
{
|
|
case S7AreaPE : return FServer->HA[srvAreaPE];
|
|
case S7AreaPA : return FServer->HA[srvAreaPA];
|
|
case S7AreaMK : return FServer->HA[srvAreaMK];
|
|
case S7AreaCT : return FServer->HA[srvAreaCT];
|
|
case S7AreaTM : return FServer->HA[srvAreaTM];
|
|
case S7AreaDB : return FServer->FindDB(index);
|
|
default : return NULL;
|
|
};
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
word TS7Worker::ReadArea(PResFunReadItem ResItemData, PReqFunReadItem ReqItemPar,
|
|
int &PDURemainder, TEv &EV)
|
|
{
|
|
PS7Area P;
|
|
word DBNum = 0;
|
|
word Elements;
|
|
longword Start, Size, ASize, AStart;
|
|
longword *PAdd;
|
|
byte BitIndex, ByteVal;
|
|
int Multiplier;
|
|
void *Source = NULL;
|
|
PSnapCriticalSection pcs;
|
|
|
|
P=NULL;
|
|
EV.EvStart =0;
|
|
EV.EvSize =0;
|
|
EV.EvRetCode =0;
|
|
EV.EvIndex =0;
|
|
|
|
EV.EvArea=ReqItemPar->Area;
|
|
// Get Pointer to selected Area
|
|
|
|
if (ReqItemPar->Area==S7AreaDB)
|
|
{
|
|
DBNum=SwapWord(ReqItemPar->DBNumber);
|
|
EV.EvIndex=DBNum;
|
|
};
|
|
|
|
if (!FServer->ResourceLess)
|
|
{
|
|
P = GetArea(ReqItemPar->Area, DBNum);
|
|
if (P == NULL)
|
|
return RA_NotFound(ResItemData, EV);
|
|
}
|
|
|
|
// Calcs the amount
|
|
Multiplier = DataSizeByte(ReqItemPar->TransportSize);
|
|
if (Multiplier==0)
|
|
return RA_OutOfRange(ResItemData, EV);
|
|
|
|
// Checks timers/counters coherence
|
|
if ((ReqItemPar->Area==S7AreaTM) ^ (ReqItemPar->TransportSize==S7WLTimer))
|
|
return RA_OutOfRange(ResItemData, EV);
|
|
|
|
if ((ReqItemPar->Area==S7AreaCT) ^ (ReqItemPar->TransportSize==S7WLCounter))
|
|
return RA_OutOfRange(ResItemData, EV);
|
|
|
|
// Calcs size
|
|
Elements = SwapWord(ReqItemPar->Length);
|
|
Size=Multiplier*Elements;
|
|
EV.EvSize=Size;
|
|
|
|
// The sum of the items must not exceed the PDU size negotiated
|
|
if (PDURemainder-Size<=0)
|
|
return RA_SizeOverPDU(ResItemData, EV);
|
|
else
|
|
PDURemainder-=Size;
|
|
|
|
// More then 1 bit is not supported by S7 CPU
|
|
if ((ReqItemPar->TransportSize==S7WLBit) && (Size>1))
|
|
return RA_OutOfRange(ResItemData, EV);
|
|
|
|
// Calcs the start point
|
|
PAdd=(longword*)(&ReqItemPar->Area); // points to area since we need 4 bytes for a pointer
|
|
Start=SwapDWord(*PAdd & 0xFFFFFF00);
|
|
|
|
// Checks if the address is not multiple of 8 when transport size is neither bit nor timer nor counter
|
|
if (
|
|
(ReqItemPar->TransportSize!=S7WLBit) &&
|
|
(ReqItemPar->TransportSize!=S7WLTimer) &&
|
|
(ReqItemPar->TransportSize!=S7WLCounter) &&
|
|
((Start % 8) !=0)
|
|
)
|
|
return RA_OutOfRange(ResItemData, EV);
|
|
|
|
// AStart is only for callback
|
|
if ((ReqItemPar->TransportSize != S7WLBit) && (ReqItemPar->TransportSize != S7WLCounter) && (ReqItemPar->TransportSize != S7WLTimer))
|
|
AStart = Start >> 3;
|
|
else
|
|
AStart = Start;
|
|
|
|
if ((ReqItemPar->TransportSize == S7WLCounter) || (ReqItemPar->TransportSize == S7WLTimer))
|
|
{
|
|
Start = Start >> 1; // 1 Timer or Counter = 2 bytes
|
|
}
|
|
else
|
|
{
|
|
BitIndex =Start & 0x07; // start bit
|
|
Start =Start >> 3; // start byte
|
|
}
|
|
|
|
EV.EvStart=Start;
|
|
|
|
// Checks bounds
|
|
if (!FServer->ResourceLess)
|
|
{
|
|
ASize = P->Size; // Area size
|
|
if (Start + Size > ASize)
|
|
return RA_OutOfRange(ResItemData, EV);
|
|
Source = P->PData + Start;
|
|
}
|
|
|
|
// Read Event (before copy data)
|
|
DoReadEvent(evcDataRead,0,EV.EvArea,EV.EvIndex,EV.EvStart,EV.EvSize);
|
|
|
|
if (FServer->ResourceLess)
|
|
{
|
|
memset(&ResItemData->Data, 0, Size);
|
|
if (!FServer->DoReadArea(ClientHandle, EV.EvArea, EV.EvIndex, AStart, Elements, ReqItemPar->TransportSize, &ResItemData->Data))
|
|
return RA_NotFound(ResItemData, EV);
|
|
}
|
|
else
|
|
{
|
|
// Lock the area
|
|
pcs = P->cs;
|
|
pcs->Enter();
|
|
// Get Data
|
|
memcpy(&ResItemData->Data, Source, Size);
|
|
// Unlock the area
|
|
pcs->Leave();
|
|
}
|
|
|
|
ResItemData->ReturnCode=0xFF;
|
|
// Set Result transport size and, for bit, performs the mask
|
|
switch (ReqItemPar->TransportSize)
|
|
{
|
|
case S7WLBit:
|
|
{
|
|
ByteVal=ResItemData->Data[0];
|
|
|
|
if ((ByteVal & BitMask[BitIndex])!=0)
|
|
ResItemData->Data[0]=0x01;
|
|
else
|
|
ResItemData->Data[0]=0x00;
|
|
|
|
ResItemData->TransportSize=TS_ResBit;
|
|
ResItemData->DataLength=SwapWord(Size);
|
|
};break;
|
|
case S7WLByte:
|
|
case S7WLWord:
|
|
case S7WLDWord:
|
|
{
|
|
ResItemData->TransportSize=TS_ResByte;
|
|
ResItemData->DataLength=SwapWord(Size*8);
|
|
};break;
|
|
case S7WLInt:
|
|
case S7WLDInt:
|
|
{
|
|
ResItemData->TransportSize=TS_ResInt;
|
|
ResItemData->DataLength=SwapWord(Size*8);
|
|
};break;
|
|
case S7WLReal:
|
|
{
|
|
ResItemData->TransportSize=TS_ResReal;
|
|
ResItemData->DataLength=SwapWord(Size);
|
|
};break;
|
|
case S7WLChar:
|
|
case S7WLTimer:
|
|
case S7WLCounter:
|
|
{
|
|
ResItemData->TransportSize=TS_ResOctet;
|
|
ResItemData->DataLength=SwapWord(Size);
|
|
};break;
|
|
default :
|
|
{
|
|
ResItemData->TransportSize=TS_ResByte;
|
|
ResItemData->DataLength=SwapWord(Size*8);
|
|
};break;
|
|
}
|
|
EV.EvRetCode=evrNoError;
|
|
return Size;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
bool TS7Worker::PerformFunctionRead()
|
|
{
|
|
PReqFunReadParams ReqParams;
|
|
PResFunReadParams ResParams;
|
|
TResFunReadData ResData;
|
|
TS7Answer23 Answer;
|
|
uintptr_t Offset;
|
|
word ItemSize;
|
|
int ItemsCount, c,
|
|
TotalSize,
|
|
PDURemainder;
|
|
TEv EV;
|
|
|
|
PDURemainder=FPDULength;
|
|
// Stage 1 : Setup pointers and initial check
|
|
ReqParams=PReqFunReadParams(pbyte(PDUH_in)+sizeof(TS7ReqHeader));
|
|
ResParams=PResFunReadParams(pbyte(&Answer)+ResHeaderSize23); // Params after the header
|
|
|
|
// trunk to 20 max items.
|
|
if (ReqParams->ItemsCount>MaxVars)
|
|
ReqParams->ItemsCount=MaxVars;
|
|
|
|
ItemsCount=ReqParams->ItemsCount;
|
|
|
|
// Stage 2 : gather data
|
|
Offset=sizeof(TResFunReadParams); // = 2
|
|
|
|
for (c = 0; c < ItemsCount; c++)
|
|
{
|
|
ResData[c]=PResFunReadItem(pbyte(ResParams)+Offset);
|
|
ItemSize=ReadArea(ResData[c],&ReqParams->Items[c],PDURemainder, EV);
|
|
|
|
// S7 doesn't xfer odd byte amount
|
|
if ((c<ItemsCount-1) && (ItemSize % 2 != 0))
|
|
ItemSize++;
|
|
|
|
Offset+=(ItemSize+4);
|
|
// For multiple items we have to create multiple events
|
|
if (ItemsCount>1)
|
|
DoEvent(evcDataRead,EV.EvRetCode,EV.EvArea,EV.EvIndex,EV.EvStart,EV.EvSize);
|
|
}
|
|
// Stage 3 : finalize the answer and send the packet
|
|
Answer.Header.P=0x32;
|
|
Answer.Header.PDUType=0x03;
|
|
Answer.Header.AB_EX=0x0000;
|
|
Answer.Header.Sequence=PDUH_in->Sequence;
|
|
Answer.Header.ParLen=SwapWord(sizeof(TResFunReadParams));
|
|
Answer.Header.Error=0x0000; // this is zero, we will find the error in ResData.ReturnCode
|
|
Answer.Header.DataLen=SwapWord(word(Offset)-2);
|
|
|
|
ResParams->FunRead =ReqParams->FunRead;
|
|
ResParams->ItemCount=ReqParams->ItemsCount;
|
|
|
|
TotalSize=ResHeaderSize23+int(Offset);
|
|
isoSendBuffer(&Answer, TotalSize);
|
|
|
|
// For single item (most likely case) it's better to work with the event after
|
|
// we sent the answer
|
|
if (ItemsCount==1)
|
|
DoEvent(evcDataRead,EV.EvRetCode,EV.EvArea,EV.EvIndex,EV.EvStart,EV.EvSize);
|
|
|
|
return true;
|
|
}
|
|
//==============================================================================
|
|
// FUNCTION WRITE
|
|
//==============================================================================
|
|
byte TS7Worker::WA_NotFound(TEv &EV)
|
|
{
|
|
EV.EvRetCode=evrErrAreaNotFound;
|
|
return Code7ResItemNotAvailable;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
byte TS7Worker::WA_InvalidTransportSize(TEv &EV)
|
|
{
|
|
EV.EvRetCode=evrErrTransportSize;
|
|
return Code7InvalidTransportSize;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
byte TS7Worker::WA_OutOfRange(TEv &EV)
|
|
{
|
|
EV.EvRetCode=evrErrOutOfRange;
|
|
return Code7AddressOutOfRange;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
byte TS7Worker::WA_DataSizeMismatch(TEv &EV)
|
|
{
|
|
EV.EvRetCode=evrDataSizeMismatch;
|
|
return Code7WriteDataSizeMismatch;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
byte TS7Worker::WriteArea(PReqFunWriteDataItem ReqItemData, PReqFunWriteItem ReqItemPar,
|
|
TEv &EV)
|
|
{
|
|
int Multiplier;
|
|
PS7Area P = NULL;
|
|
word DBNum = 0;
|
|
word Elements;
|
|
longword *PAdd;
|
|
PSnapCriticalSection pcs;
|
|
longword Start, Size, ASize, DataLen, AStart;
|
|
pbyte Target = NULL;
|
|
byte BitIndex;
|
|
|
|
EV.EvStart =0;
|
|
EV.EvSize =0;
|
|
EV.EvRetCode =evrNoError;
|
|
EV.EvIndex =0;
|
|
|
|
EV.EvArea=ReqItemPar->Area;
|
|
// Get Pointer to selected Area
|
|
if (ReqItemPar->Area==S7AreaDB)
|
|
{
|
|
DBNum=SwapWord(ReqItemPar->DBNumber);
|
|
EV.EvIndex=DBNum;
|
|
};
|
|
|
|
if (!FServer->ResourceLess)
|
|
{
|
|
P=GetArea(ReqItemPar->Area, DBNum);
|
|
if (P==NULL)
|
|
return WA_NotFound(EV);
|
|
}
|
|
|
|
// Calcs the amount
|
|
Multiplier = DataSizeByte(ReqItemPar->TransportSize);
|
|
if (Multiplier==0)
|
|
return WA_InvalidTransportSize(EV);
|
|
|
|
// Checks timers/counters coherence
|
|
if ((ReqItemPar->Area==S7AreaTM) ^ (ReqItemPar->TransportSize==S7WLTimer))
|
|
return WA_OutOfRange(EV);
|
|
|
|
if ((ReqItemPar->Area==S7AreaCT) ^ (ReqItemPar->TransportSize==S7WLCounter))
|
|
return WA_OutOfRange(EV);
|
|
|
|
// Calcs size
|
|
Elements = SwapWord(ReqItemPar->Length);
|
|
Size = Multiplier*Elements;
|
|
EV.EvSize=Size;
|
|
|
|
// More) 1 bit is not supported by S7 CPU
|
|
if ((ReqItemPar->TransportSize==S7WLBit) && (Size>1))
|
|
return WA_OutOfRange(EV);
|
|
|
|
// Calcs the start point ??
|
|
PAdd=(longword*)&ReqItemPar->Area; // points to area since we need 4 bytes for a pointer
|
|
Start=SwapDWord(*PAdd & 0xFFFFFF00);
|
|
|
|
// Checks if the address is not multiple of 8 when transport size is neither bit nor timer nor counter
|
|
if (
|
|
(ReqItemPar->TransportSize!=S7WLBit) &&
|
|
(ReqItemPar->TransportSize!=S7WLTimer) &&
|
|
(ReqItemPar->TransportSize!=S7WLCounter) &&
|
|
((Start % 8) !=0)
|
|
)
|
|
return WA_OutOfRange(EV);
|
|
|
|
// AStart is only for callback
|
|
if ((ReqItemPar->TransportSize != S7WLBit) && (ReqItemPar->TransportSize != S7WLCounter) && (ReqItemPar->TransportSize != S7WLTimer))
|
|
AStart = Start >> 3;
|
|
else
|
|
AStart = Start;
|
|
|
|
if ((ReqItemPar->TransportSize == S7WLCounter) || (ReqItemPar->TransportSize == S7WLTimer))
|
|
{
|
|
Start = Start >> 1; // 1 Timer or Counter = 2 bytes
|
|
}
|
|
else
|
|
{
|
|
BitIndex = Start & 0x07; // start bit
|
|
Start = Start >> 3; // start byte
|
|
}
|
|
EV.EvStart =Start;
|
|
|
|
if (!FServer->ResourceLess)
|
|
{
|
|
// Checks bounds
|
|
ASize = P->Size; // Area size
|
|
if (Start + Size > ASize)
|
|
return WA_OutOfRange(EV);
|
|
Target = pbyte(P->PData + Start);
|
|
}
|
|
// Checks data size coherence
|
|
DataLen=SwapWord(ReqItemData->DataLength);
|
|
|
|
if ((ReqItemData->TransportSize!=TS_ResOctet) && (ReqItemData->TransportSize!=TS_ResReal) && (ReqItemData->TransportSize!=TS_ResBit))
|
|
DataLen=DataLen / 8;
|
|
|
|
if (DataLen!=Size)
|
|
return WA_DataSizeMismatch(EV);
|
|
|
|
if (FServer->ResourceLess)
|
|
{
|
|
if (!FServer->DoWriteArea(ClientHandle, EV.EvArea, EV.EvIndex, AStart, Elements, ReqItemPar->TransportSize, &ReqItemData->Data[0]))
|
|
return WA_NotFound(EV);
|
|
}
|
|
else
|
|
{
|
|
if (ReqItemPar->TransportSize==S7WLBit)
|
|
{
|
|
if ((ReqItemData->Data[0] & 0x01) != 0) // bit set
|
|
*Target=*Target | BitMask[BitIndex];
|
|
else // bit reset
|
|
*Target=*Target & (~BitMask[BitIndex]);
|
|
}
|
|
else {
|
|
// Lock the area
|
|
pcs = P->cs;
|
|
pcs->Enter();
|
|
// Write Data
|
|
memcpy(Target, &ReqItemData->Data[0], Size);
|
|
pcs->Leave();
|
|
};
|
|
}
|
|
|
|
return 0xFF;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
bool TS7Worker::PerformFunctionWrite()
|
|
{
|
|
PReqFunWriteParams ReqParams;
|
|
TReqFunWriteData ReqData;
|
|
PResFunWrite ResData;
|
|
TS7Answer23 Answer;
|
|
int L;
|
|
|
|
uintptr_t StartData;
|
|
int c, ItemsCount;
|
|
int ResDSize;
|
|
TEv EV;
|
|
|
|
// Stage 1 : Setup pointers and initial check
|
|
ReqParams=PReqFunWriteParams(pbyte(PDUH_in)+sizeof(TS7ReqHeader));
|
|
ResData =PResFunWrite(pbyte(&Answer)+ResHeaderSize23);
|
|
|
|
StartData=sizeof(TS7ReqHeader)+SwapWord(PDUH_in->ParLen);
|
|
|
|
ItemsCount=ReqParams->ItemsCount;
|
|
ResDSize =ResHeaderSize23+2+ItemsCount;
|
|
for (c = 0; c < ItemsCount; c++)
|
|
{
|
|
ReqData[c]=PReqFunWriteDataItem(pbyte(PDUH_in)+StartData);
|
|
|
|
if ((ReqParams->Items[c].TransportSize == S7WLTimer) || (ReqParams->Items[c].TransportSize == S7WLCounter) || (ReqParams->Items[c].TransportSize == S7WLBit))
|
|
L = SwapWord(ReqData[c]->DataLength);
|
|
else
|
|
L = (SwapWord(ReqData[c]->DataLength) / 8);
|
|
|
|
StartData+=L+4;
|
|
// the datalength is always even
|
|
if ( L % 2 != 0) StartData++;
|
|
}
|
|
|
|
ResData->FunWrite =pduFuncWrite;
|
|
ResData->ItemCount=ReqParams->ItemsCount;
|
|
|
|
// Stage 2 : Write data
|
|
for (c = 0; c < ItemsCount; c++)
|
|
{
|
|
ResData->Data[c]=WriteArea(ReqData[c],&ReqParams->Items[c], EV);
|
|
// For multiple items we have to create multiple events
|
|
if (ItemsCount>1)
|
|
DoEvent(evcDataWrite,EV.EvRetCode,EV.EvArea,EV.EvIndex,EV.EvStart,EV.EvSize);
|
|
}
|
|
|
|
// Stage 3 : finalize 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(0x02);
|
|
Answer.Header.Error=0x0000; // this is zero, we will find the error in ResData.ReturnCode if any
|
|
Answer.Header.DataLen=SwapWord(ItemsCount);
|
|
|
|
isoSendBuffer(&Answer,ResDSize);
|
|
// For single item (most likely case) it's better to fire the event after
|
|
// we sent the answer
|
|
if (ItemsCount==1)
|
|
DoEvent(evcDataWrite,EV.EvRetCode,EV.EvArea,EV.EvIndex,EV.EvStart,EV.EvSize);
|
|
return true;
|
|
}
|
|
//==============================================================================
|
|
// FUNCTION NEGOTIATE
|
|
//==============================================================================
|
|
bool TS7Worker::PerformFunctionNegotiate()
|
|
{
|
|
PReqFunNegotiateParams ReqParams;
|
|
PResFunNegotiateParams ResParams;
|
|
word ReqLen;
|
|
TS7Answer23 Answer;
|
|
int Size;
|
|
|
|
// Setup pointers
|
|
ReqParams=PReqFunNegotiateParams(pbyte(PDUH_in)+sizeof(TS7ReqHeader));
|
|
ResParams=PResFunNegotiateParams(pbyte(&Answer)+sizeof(TS7ResHeader23));
|
|
// 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 end of the header
|
|
ResParams->FunNegotiate=pduNegotiate;
|
|
ResParams->Unknown=0x0;
|
|
// We offer the same
|
|
ResParams->ParallelJobs_1=ReqParams->ParallelJobs_1;
|
|
ResParams->ParallelJobs_2=ReqParams->ParallelJobs_2;
|
|
|
|
if (FServer->ForcePDU == 0)
|
|
{
|
|
ReqLen = SwapWord(ReqParams->PDULength);
|
|
if (ReqLen<MinPduSize)
|
|
ResParams->PDULength = SwapWord(MinPduSize);
|
|
else
|
|
if (ReqLen>IsoPayload_Size)
|
|
ResParams->PDULength = SwapWord(IsoPayload_Size);
|
|
else
|
|
ResParams->PDULength = ReqParams->PDULength;
|
|
}
|
|
else
|
|
ResParams->PDULength = SwapWord(FServer->ForcePDU);
|
|
|
|
FPDULength=SwapWord(ResParams->PDULength); // Stores the value
|
|
// Sends the answer
|
|
Size=sizeof(TS7ResHeader23) + sizeof(TResFunNegotiateParams);
|
|
isoSendBuffer(&Answer, Size);
|
|
// Store the event
|
|
DoEvent(evcNegotiatePDU, evrNoError, FPDULength, 0, 0, 0);
|
|
return true;
|
|
}
|
|
//==============================================================================
|
|
// FUNCTION CONTROL
|
|
//==============================================================================
|
|
bool TS7Worker::PerformFunctionControl(byte PduFun)
|
|
{
|
|
TS7Answer23 Answer;
|
|
PResFunCtrl ResParams;
|
|
word ParLen;
|
|
word CtrlCode;
|
|
|
|
// Setup pointer
|
|
ResParams=PResFunCtrl(pbyte(&Answer)+sizeof(TS7ResHeader23));
|
|
// 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(0x0001); // We send only Res fun without para
|
|
Answer.Header.DataLen=0x0000;
|
|
Answer.Header.Error=0x0000;
|
|
ResParams->ResFun=PduFun;
|
|
ResParams->para =0x00;
|
|
|
|
ParLen=SwapWord(PDUH_in->ParLen);
|
|
if (PduFun==pduStop)
|
|
CtrlCode=CodeControlStop;
|
|
else
|
|
{
|
|
switch (ParLen)
|
|
{
|
|
case 16 : CtrlCode=CodeControlCompress; break;
|
|
case 18 : CtrlCode=CodeControlCpyRamRom; break;
|
|
case 20 : CtrlCode=CodeControlWarmStart; break;
|
|
case 22 : CtrlCode=CodeControlColdStart; break;
|
|
case 26 : CtrlCode=CodeControlInsDel; break;
|
|
default : CtrlCode=CodeControlUnknown;
|
|
}
|
|
}
|
|
// Sends the answer
|
|
isoSendBuffer(&Answer,sizeof(TS7ResHeader23)+1);
|
|
// Stores the event
|
|
DoEvent(evcControl,0,CtrlCode,0,0,0);
|
|
|
|
if ((CtrlCode==CodeControlWarmStart) || (CtrlCode==CodeControlColdStart))
|
|
FServer->CpuStatus=S7CpuStatusRun;
|
|
|
|
if (CtrlCode==CodeControlStop)
|
|
FServer->CpuStatus=S7CpuStatusStop;
|
|
|
|
return true;
|
|
}
|
|
//==============================================================================
|
|
// FUNCTION UPLOAD
|
|
//==============================================================================
|
|
bool TS7Worker::PerformFunctionUpload()
|
|
{
|
|
TS7Answer23 Answer;
|
|
|
|
// Upload is not implemented, however to avoid that S7 manager hangs
|
|
// we simulate a cpu read/write protected.
|
|
// We can see the directory but can't upload/download anything
|
|
|
|
// Prepares the answer
|
|
Answer.Header.P=0x32;
|
|
Answer.Header.PDUType =pduResponse;
|
|
Answer.Header.AB_EX=0x0000;
|
|
Answer.Header.Sequence=PDUH_in->Sequence;
|
|
Answer.Header.ParLen=0;
|
|
Answer.Header.DataLen=0;
|
|
Answer.Header.Error=SwapWord(Code7NeedPassword);
|
|
// Sends the answer
|
|
isoSendBuffer(&Answer,sizeof(TS7ResHeader23));
|
|
|
|
DoEvent(evcUpload,evrCannotUpload,evsStartUpload,0,0,0);
|
|
return true;
|
|
}
|
|
//==============================================================================
|
|
// FUNCTION DOWNLOAD
|
|
//==============================================================================
|
|
bool TS7Worker::PerformFunctionDownload()
|
|
{
|
|
TS7Answer23 Answer;
|
|
|
|
// Download is not implemented, however to avoid that S7 manager hangs
|
|
// we simulate a cpu read/write protected.
|
|
// We can see the directory but can't upload/download anything
|
|
|
|
// Prepares the answer
|
|
Answer.Header.P=0x32;
|
|
Answer.Header.PDUType =pduResponse;
|
|
Answer.Header.AB_EX=0x0000;
|
|
Answer.Header.Sequence=PDUH_in->Sequence;
|
|
Answer.Header.ParLen=0;
|
|
Answer.Header.DataLen=0;
|
|
Answer.Header.Error=SwapWord(Code7NeedPassword);
|
|
// Sends the answer
|
|
isoSendBuffer(&Answer,sizeof(TS7ResHeader23));
|
|
|
|
DoEvent(evcUpload,evrCannotDownload, evsStartDownload,0,0,0);
|
|
return true;
|
|
}
|
|
//==============================================================================
|
|
// FUNCTIONS PROGRAMMER AND CYCLIC DATA (NOT IMPLEMENTED...yet)
|
|
//==============================================================================
|
|
bool TS7Worker::PerformGroupProgrammer()
|
|
{
|
|
DoEvent(evcPDUincoming,evrNotImplemented,grProgrammer,0,0,0);
|
|
return true;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
bool TS7Worker::PerformGroupCyclicData()
|
|
{
|
|
DoEvent(evcPDUincoming,evrNotImplemented,grCyclicData,0,0,0);
|
|
return true;
|
|
}
|
|
//==============================================================================
|
|
// BLOCK(S) INFO FUNCTIONS
|
|
//==============================================================================
|
|
void TS7Worker::BLK_ListAll(TCB &CB)
|
|
{
|
|
PDataFunListAll Data;
|
|
int TotalSize;
|
|
|
|
TotalSize = ResHeaderSize17+sizeof(TResFunGetBlockInfo)+sizeof(TDataFunListAll);
|
|
// Prepares the answer
|
|
CB.Answer.Header.P=0x32;
|
|
CB.Answer.Header.PDUType=PduType_userdata;
|
|
CB.Answer.Header.AB_EX=0x0000;
|
|
CB.Answer.Header.Sequence=PDUH_in->Sequence;
|
|
CB.Answer.Header.ParLen =SwapWord(sizeof(TResFunGetBlockInfo));
|
|
CB.Answer.Header.DataLen=SwapWord(sizeof(TDataFunListAll));
|
|
|
|
CB.ResParams->Head[0]=CB.ReqParams->Head[0];
|
|
CB.ResParams->Head[1]=CB.ReqParams->Head[1];
|
|
CB.ResParams->Head[2]=CB.ReqParams->Head[2];
|
|
CB.ResParams->Plen =0x08;
|
|
CB.ResParams->Uk =0x12;
|
|
CB.ResParams->Tg =0x83; // Type response, group functions info
|
|
CB.ResParams->SubFun=SFun_ListAll;
|
|
|
|
CB.ResParams->Seq =CB.ReqParams->Seq;
|
|
CB.ResParams->Rsvd =0x0000;
|
|
CB.ResParams->ErrNo =0x0000;
|
|
|
|
Data=PDataFunListAll(pbyte(&CB.Answer)+ResHeaderSize17+sizeof(TResFunGetBlockInfo));
|
|
Data->RetVal=0xFF;
|
|
Data->TRSize=TS_ResOctet;
|
|
Data->Length=SwapWord(28); // 28 = Size of TDataFunListAll.Blocks
|
|
// Fill elements, only DB will have a valid number
|
|
Data->Blocks[0].Zero=0x30;
|
|
Data->Blocks[0].BType=Block_OB;
|
|
Data->Blocks[0].BCount=0x0000; // We don't have OBs
|
|
Data->Blocks[1].Zero=0x30;
|
|
Data->Blocks[1].BType=Block_FB;
|
|
Data->Blocks[1].BCount=0x0000; // We don't have FBs
|
|
Data->Blocks[2].Zero=0x30;
|
|
Data->Blocks[2].BType=Block_FC;
|
|
Data->Blocks[2].BCount=0x0000; // We don't have FCs
|
|
Data->Blocks[3].Zero=0x30;
|
|
Data->Blocks[3].BType=Block_DB;
|
|
Data->Blocks[3].BCount=SwapWord(FServer->DBCount); // Most likely we HAVE DBs
|
|
Data->Blocks[4].Zero=0x30;
|
|
Data->Blocks[4].BType=Block_SDB;
|
|
Data->Blocks[4].BCount=0x0000; // We don't have SDBs
|
|
Data->Blocks[5].Zero=0x30;
|
|
Data->Blocks[5].BType=Block_SFC;
|
|
Data->Blocks[5].BCount=0x0000; // We don't have SFCs
|
|
Data->Blocks[6].Zero=0x30;
|
|
Data->Blocks[6].BType=Block_SFB;
|
|
Data->Blocks[6].BCount=0x0000; // We don't have SFBs
|
|
// Sends
|
|
isoSendBuffer(&CB.Answer,TotalSize);
|
|
DoEvent(evcDirectory, 0, evsGetBlockList, 0, 0, 0);
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
void TS7Worker::BLK_NoResource_ListBoT(PDataFunGetBot Data, TCB &CB)
|
|
{
|
|
CB.DataLength =4;
|
|
DBCnt =0; // Reset counter
|
|
CB.Answer.Header.DataLen=SwapWord(CB.DataLength);
|
|
CB.ResParams->ErrNo =0x0ED2; // function in error
|
|
Data->RetVal =0x0A; // No resource available
|
|
Data->TSize =0x00; // No transport size;
|
|
Data->DataLen =0x0000; // No data;
|
|
CB.evError =evrResNotFound;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
void TS7Worker::BLK_ListBoT(byte BlockType, bool Start, TCB &CB)
|
|
{
|
|
PDataFunGetBot Data;
|
|
int MaxItems, TotalSize, cnt;
|
|
int HiBound = FServer->DBLimit+1;
|
|
|
|
CB.evError=0;
|
|
MaxItems=(FPDULength - 32) / 4;
|
|
// Prepares the answer
|
|
CB.Answer.Header.P=0x32;
|
|
CB.Answer.Header.PDUType=PduType_userdata;
|
|
CB.Answer.Header.AB_EX=0x0000;
|
|
CB.Answer.Header.Sequence=PDUH_in->Sequence;
|
|
CB.Answer.Header.ParLen =SwapWord(sizeof(TResFunGetBlockInfo));
|
|
|
|
CB.ResParams->Head[0]=CB.ReqParams->Head[0];
|
|
CB.ResParams->Head[1]=CB.ReqParams->Head[1];
|
|
CB.ResParams->Head[2]=CB.ReqParams->Head[2];
|
|
CB.ResParams->Plen =0x08;
|
|
CB.ResParams->Uk =0x12;
|
|
CB.ResParams->Tg =0x83; // Type response, group functions info
|
|
CB.ResParams->SubFun=SFun_ListBoT;
|
|
CB.ResParams->Seq =CB.ReqParams->Seq;
|
|
CB.ResParams->Rsvd =0x0000;
|
|
Data=PDataFunGetBot(pbyte(&CB.Answer)+ResHeaderSize17+sizeof(TResFunGetBlockInfo));
|
|
|
|
if (BlockType==Block_DB)
|
|
{
|
|
cnt =0; // Local couter
|
|
if (Start)
|
|
DBCnt=-1; // Global counter
|
|
|
|
if (FServer->DBCount>0)
|
|
{
|
|
while ((cnt<MaxItems) && (DBCnt<HiBound))
|
|
{
|
|
DBCnt++;
|
|
if (FServer->DB[DBCnt]!=NULL)
|
|
{
|
|
Data->Items[cnt].BlockNum=SwapWord(FServer->DB[DBCnt]->Number);
|
|
Data->Items[cnt].Unknown =0x22;
|
|
Data->Items[cnt].BlockLang=0x05;
|
|
cnt++;
|
|
};
|
|
};
|
|
|
|
if ((cnt<MaxItems) || (DBCnt==HiBound))
|
|
{
|
|
DBCnt=0; // Finished
|
|
CB.ResParams->Rsvd=0x0023;
|
|
}
|
|
else
|
|
CB.ResParams->Rsvd=0x0123;
|
|
|
|
if (cnt>0)
|
|
{
|
|
CB.ResParams->ErrNo =0x0000;
|
|
Data->TSize =TS_ResOctet;
|
|
Data->RetVal=0xFF;
|
|
CB.DataLength=4+(cnt*word(sizeof(TDataFunGetBotItem)));
|
|
CB.Answer.Header.DataLen=SwapWord(CB.DataLength);
|
|
Data->DataLen=SwapWord(CB.DataLength-4);
|
|
}
|
|
else
|
|
BLK_NoResource_ListBoT(Data, CB);
|
|
}
|
|
else
|
|
BLK_NoResource_ListBoT(Data, CB);
|
|
}
|
|
else // we store only DBs
|
|
BLK_NoResource_ListBoT(Data, CB);
|
|
|
|
TotalSize = ResHeaderSize17+sizeof(TResFunGetBlockInfo)+CB.DataLength;
|
|
isoSendBuffer(&CB.Answer,TotalSize);
|
|
if (Start)
|
|
DoEvent(evcDirectory, CB.evError, evsStartListBoT, BlockType, 0, 0);
|
|
else
|
|
DoEvent(evcDirectory, CB.evError, evsListBoT, BlockType, 0, 0);
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
void TS7Worker::BLK_NoResource_GetBlkInfo(PResDataBlockInfo Data, TCB &CB)
|
|
{
|
|
CB.DataLength =4;
|
|
CB.Answer.Header.DataLen=SwapWord(CB.DataLength);
|
|
CB.ResParams->ErrNo =0x09D2; // function in error
|
|
Data->RetVal =0x0A; // No resource available
|
|
Data->TSize =0x00; // No transport size;
|
|
Data->Length =0x0000; // No data;
|
|
CB.evError =evrResNotFound;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
void TS7Worker::BLK_GetBlockNum_GetBlkInfo(int &BlkNum, PReqDataBlockInfo ReqData)
|
|
{
|
|
BlkNum = (ReqData->AsciiBlk[4] - 0x30) +
|
|
(ReqData->AsciiBlk[3] - 0x30) * 10 +
|
|
(ReqData->AsciiBlk[2] - 0x30) * 100 +
|
|
(ReqData->AsciiBlk[1] - 0x30) * 1000 +
|
|
(ReqData->AsciiBlk[0] - 0x30) * 10000;
|
|
|
|
if (BlkNum>65535)
|
|
BlkNum=-1;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
void TS7Worker::BLK_DoBlockInfo_GetBlkInfo(PS7Area DB, PResDataBlockInfo Data, TCB &CB)
|
|
{
|
|
// Prepares the answer
|
|
CB.Answer.Header.P=0x32;
|
|
CB.Answer.Header.PDUType=PduType_userdata;
|
|
CB.Answer.Header.AB_EX=0x0000;
|
|
CB.Answer.Header.Sequence=PDUH_in->Sequence;
|
|
CB.Answer.Header.ParLen =SwapWord(sizeof(TResFunGetBlockInfo));
|
|
|
|
CB.ResParams->Head[0]=CB.ReqParams->Head[0];
|
|
CB.ResParams->Head[1]=CB.ReqParams->Head[1];
|
|
CB.ResParams->Head[2]=CB.ReqParams->Head[2];
|
|
CB.ResParams->Plen =0x08;
|
|
CB.ResParams->Uk =0x12;
|
|
CB.ResParams->Tg =0x83; // Type response, group functions info
|
|
CB.ResParams->SubFun=SFun_BlkInfo;
|
|
CB.ResParams->Seq =CB.ReqParams->Seq;
|
|
CB.ResParams->Rsvd =0x0000;
|
|
CB.ResParams->ErrNo =0x0000;
|
|
|
|
CB.DataLength =sizeof(TResDataBlockInfo);
|
|
CB.Answer.Header.DataLen=SwapWord(CB.DataLength);
|
|
CB.ResParams->ErrNo =0x0000;
|
|
|
|
Data->RetVal =0xFF;
|
|
Data->TSize =TS_ResOctet;
|
|
Data->Length =SwapWord(78); // this struct - RetValData->Tsize and length
|
|
Data->Cst_b =0x01;
|
|
Data->BlkType =0x00;
|
|
Data->Cst_w1 =0x4A00;
|
|
Data->Cst_w2 =0x0022;
|
|
Data->Cst_pp =0x7070;
|
|
Data->Unknown_1 =0x01;
|
|
Data->BlkFlags =0x01;
|
|
Data->BlkLang =BlockLangDB;
|
|
Data->SubBlkType =0x0A;
|
|
Data->CodeTime_dy =SwapWord(5800);// Nov/18/1999 my princess's birthdate
|
|
Data->IntfTime_dy =Data->CodeTime_dy;
|
|
Data->LocDataLen =0x0000;
|
|
Data->BlkNumber =SwapWord(DB->Number);
|
|
Data->SbbLen =0x1400;
|
|
Data->AddLen =0x0000;
|
|
Data->MC7Len =SwapWord(DB->Size);
|
|
Data->LenLoadMem =SwapDWord(DB->Size+92);
|
|
Data->Version =0x01;
|
|
Data->Unknown_2 =0x00;
|
|
Data->BlkChksum =0x0000;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
void TS7Worker::BLK_GetBlkInfo(TCB &CB)
|
|
{
|
|
PReqDataBlockInfo ReqData;
|
|
PResDataBlockInfo Data;
|
|
int BlkNum;
|
|
PS7Area BlkDB;
|
|
byte BlkTypeInfo;
|
|
int TotalSize;
|
|
|
|
CB.evError=0;
|
|
Data =PResDataBlockInfo(pbyte(&CB.Answer)+ResHeaderSize17+sizeof(TResFunGetBlockInfo));
|
|
ReqData=PReqDataBlockInfo(pbyte(PDUH_in)+ReqHeaderSize+sizeof(TReqFunGetBlockInfo));
|
|
memset(Data,0,sizeof(TResDataBlockInfo)); // many fields are 0
|
|
|
|
BLK_GetBlockNum_GetBlkInfo(BlkNum, ReqData);
|
|
BlkTypeInfo=ReqData->BlkType;
|
|
if (BlkTypeInfo==Block_DB)
|
|
{
|
|
if (BlkNum>=0)
|
|
{
|
|
BlkDB=FServer->FindDB(BlkNum);
|
|
if (BlkDB!=NULL)
|
|
BLK_DoBlockInfo_GetBlkInfo(BlkDB, Data, CB);
|
|
else
|
|
BLK_NoResource_GetBlkInfo(Data, CB);
|
|
}
|
|
else
|
|
BLK_NoResource_GetBlkInfo(Data, CB);
|
|
}
|
|
else
|
|
BLK_NoResource_GetBlkInfo(Data, CB);
|
|
|
|
TotalSize = ResHeaderSize17+sizeof(TResFunGetBlockInfo)+sizeof(TResDataBlockInfo);
|
|
isoSendBuffer(&CB.Answer, TotalSize);
|
|
DoEvent(evcDirectory,CB.evError,evsGetBlockInfo,BlkTypeInfo,BlkNum,0);
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
bool TS7Worker::PerformGroupBlockInfo()
|
|
{
|
|
TCB CB;
|
|
pbyte BlockType;
|
|
|
|
// Setup pointers
|
|
CB.ReqParams=PReqFunGetBlockInfo(pbyte(PDUH_in)+ReqHeaderSize);
|
|
CB.ResParams=PResFunGetBlockInfo(pbyte(&CB.Answer)+ResHeaderSize17);
|
|
BlockType =pbyte(PDUH_in)+23;
|
|
|
|
switch (CB.ReqParams->SubFun)
|
|
{
|
|
case SFun_ListAll : BLK_ListAll(CB); break; // List all blocks
|
|
case SFun_ListBoT :
|
|
{
|
|
if (CB.ReqParams->Plen==4)
|
|
{
|
|
LastBlk=*BlockType;
|
|
BLK_ListBoT(*BlockType, true, CB); // start sequence from beginning
|
|
}
|
|
else
|
|
BLK_ListBoT(LastBlk, false, CB); // Continue sequence
|
|
}; break;
|
|
case SFun_BlkInfo : BLK_GetBlkInfo(CB); // Get Block info
|
|
}
|
|
return true;
|
|
}
|
|
//==============================================================================
|
|
// FUNCTION SZL
|
|
//==============================================================================
|
|
void TS7Worker::SZLNotAvailable()
|
|
{
|
|
SZL.Answer.Header.DataLen=SwapWord(sizeof(SZLNotAvail));
|
|
SZL.ResParams->Err = 0x02D4;
|
|
memcpy(SZL.ResData, &SZLNotAvail, sizeof(SZLNotAvail));
|
|
isoSendBuffer(&SZL.Answer,26);
|
|
SZL.SZLDone=false;
|
|
}
|
|
void TS7Worker::SZLSystemState()
|
|
{
|
|
SZL.Answer.Header.DataLen=SwapWord(sizeof(SZLSysState));
|
|
SZL.ResParams->Err =0x0000;
|
|
memcpy(SZL.ResData,&SZLNotAvail,sizeof(SZLSysState));
|
|
isoSendBuffer(&SZL.Answer,28);
|
|
SZL.SZLDone=true;
|
|
|
|
}
|
|
void TS7Worker::SZLData(void *P, int len)
|
|
{
|
|
int MaxSzl=FPDULength-22;
|
|
|
|
if (len>MaxSzl) {
|
|
len=MaxSzl;
|
|
}
|
|
|
|
SZL.Answer.Header.DataLen=SwapWord(word(len));
|
|
SZL.ResParams->Err =0x0000;
|
|
SZL.ResParams->resvd=0x0000; // this is the end, no more packets
|
|
memcpy(SZL.ResData, P, len);
|
|
|
|
SZL.ResData[2]=((len-4)>>8) & 0xFF;
|
|
SZL.ResData[3]=(len-4) & 0xFF;
|
|
|
|
isoSendBuffer(&SZL.Answer,22+len);
|
|
SZL.SZLDone=true;
|
|
}
|
|
// this block is dynamic (contains date/time and cpu status)
|
|
void TS7Worker::SZL_ID424()
|
|
{
|
|
PS7Time PTime;
|
|
pbyte PStatus;
|
|
|
|
SZL.Answer.Header.DataLen=SwapWord(sizeof(SZL_ID_0424_IDX_XXXX));
|
|
SZL.ResParams->Err =0x0000;
|
|
PTime=PS7Time(pbyte(SZL.ResData)+24);
|
|
PStatus =pbyte(SZL.ResData)+15;
|
|
memcpy(SZL.ResData,&SZL_ID_0424_IDX_XXXX,sizeof(SZL_ID_0424_IDX_XXXX));
|
|
FillTime(PTime);
|
|
*PStatus=FServer->CpuStatus;
|
|
SZL.SZLDone=true;
|
|
isoSendBuffer(&SZL.Answer,22+sizeof(SZL_ID_0424_IDX_XXXX));
|
|
}
|
|
|
|
void TS7Worker::SZL_ID131_IDX003()
|
|
{
|
|
word len = sizeof(SZL_ID_0131_IDX_0003);
|
|
SZL.Answer.Header.DataLen=SwapWord(len);
|
|
SZL.ResParams->Err =0x0000;
|
|
SZL.ResParams->resvd=0x0000; // this is the end, no more packets
|
|
memcpy(SZL.ResData, &SZL_ID_0131_IDX_0003, len);
|
|
// Set the max consistent data window to PDU size
|
|
SZL.ResData[18]=((FPDULength)>>8) & 0xFF;
|
|
SZL.ResData[19]=(FPDULength) & 0xFF;
|
|
|
|
isoSendBuffer(&SZL.Answer,22+len);
|
|
SZL.SZLDone=true;
|
|
}
|
|
|
|
bool TS7Worker::PerformGroupSZL()
|
|
{
|
|
SZL.SZLDone=false;
|
|
// Setup pointers
|
|
SZL.ReqParams=PReqFunReadSZLFirst(pbyte(PDUH_in)+ReqHeaderSize);
|
|
SZL.ResParams=PS7ResParams7(pbyte(&SZL.Answer)+ResHeaderSize17);
|
|
SZL.ResData =pbyte(&SZL.Answer)+ResHeaderSize17+sizeof(TS7Params7);
|
|
// Prepare Answer header
|
|
SZL.Answer.Header.P=0x32;
|
|
SZL.Answer.Header.PDUType=PduType_userdata;
|
|
SZL.Answer.Header.AB_EX=0x0000;
|
|
SZL.Answer.Header.Sequence=PDUH_in->Sequence;
|
|
SZL.Answer.Header.ParLen =SwapWord(sizeof(TS7Params7));
|
|
|
|
SZL.ResParams->Head[0]=SZL.ReqParams->Head[0];
|
|
SZL.ResParams->Head[1]=SZL.ReqParams->Head[1];
|
|
SZL.ResParams->Head[2]=SZL.ReqParams->Head[2];
|
|
SZL.ResParams->Plen =0x08;
|
|
SZL.ResParams->Uk =0x12;
|
|
SZL.ResParams->Tg =0x84; // Type response + group szl
|
|
SZL.ResParams->SubFun=SZL.ReqParams->SubFun;
|
|
SZL.ResParams->Seq =SZL.ReqParams->Seq;
|
|
SZL.ResParams->resvd=0x0000; // this is the end, no more packets
|
|
|
|
// only two subfunction are defined : 0x01 read, 0x02 system state
|
|
if (SZL.ResParams->SubFun==0x02) // 0x02 = subfunction system state
|
|
{
|
|
SZLSystemState();
|
|
return true;
|
|
};
|
|
if (SZL.ResParams->SubFun!=0x01)
|
|
{
|
|
SZLNotAvailable();
|
|
return true;
|
|
};
|
|
// From here we assume subfunction = 0x01
|
|
SZL.ReqData=PS7ReqSZLData(pbyte(PDUH_in)+ReqHeaderSize+sizeof(TReqFunReadSZLFirst));// Data after params
|
|
|
|
SZL.ID=SwapWord(SZL.ReqData->ID);
|
|
SZL.Index=SwapWord(SZL.ReqData->Index);
|
|
|
|
// Switch prebuilt Data Bank (they come from a physical CPU)
|
|
switch (SZL.ID)
|
|
{
|
|
case 0x0000 : SZLData(&SZL_ID_0000_IDX_XXXX,sizeof(SZL_ID_0000_IDX_XXXX));break;
|
|
case 0x0F00 : SZLData(&SZL_ID_0F00_IDX_XXXX,sizeof(SZL_ID_0F00_IDX_XXXX));break;
|
|
case 0x0002 : SZLData(&SZL_ID_0002_IDX_XXXX,sizeof(SZL_ID_0002_IDX_XXXX));break;
|
|
case 0x0011 : SZLData(&SZL_ID_0011_IDX_XXXX,sizeof(SZL_ID_0011_IDX_XXXX));break;
|
|
case 0x0012 : SZLData(&SZL_ID_0012_IDX_XXXX,sizeof(SZL_ID_0012_IDX_XXXX));break;
|
|
case 0x0013 : SZLData(&SZL_ID_0013_IDX_XXXX,sizeof(SZL_ID_0013_IDX_XXXX));break;
|
|
case 0x0014 : SZLData(&SZL_ID_0014_IDX_XXXX,sizeof(SZL_ID_0014_IDX_XXXX));break;
|
|
case 0x0015 : SZLData(&SZL_ID_0015_IDX_XXXX,sizeof(SZL_ID_0015_IDX_XXXX));break;
|
|
case 0x0F14 : SZLData(&SZL_ID_0F14_IDX_XXXX,sizeof(SZL_ID_0F14_IDX_XXXX));break;
|
|
case 0x0019 : SZLData(&SZL_ID_0019_IDX_XXXX,sizeof(SZL_ID_0019_IDX_XXXX));break;
|
|
case 0x0F19 : SZLData(&SZL_ID_0F19_IDX_XXXX,sizeof(SZL_ID_0F19_IDX_XXXX));break;
|
|
case 0x001C : SZLData(&SZL_ID_001C_IDX_XXXX,sizeof(SZL_ID_001C_IDX_XXXX));break;
|
|
case 0x0F1C : SZLData(&SZL_ID_0F1C_IDX_XXXX,sizeof(SZL_ID_0F1C_IDX_XXXX));break;
|
|
case 0x0036 : SZLData(&SZL_ID_0036_IDX_XXXX,sizeof(SZL_ID_0036_IDX_XXXX));break;
|
|
case 0x0F36 : SZLData(&SZL_ID_0F36_IDX_XXXX,sizeof(SZL_ID_0F36_IDX_XXXX));break;
|
|
case 0x0025 : SZLData(&SZL_ID_0025_IDX_XXXX,sizeof(SZL_ID_0025_IDX_XXXX));break;
|
|
case 0x0F25 : SZLData(&SZL_ID_0F25_IDX_XXXX,sizeof(SZL_ID_0F25_IDX_XXXX));break;
|
|
case 0x0037 : SZLData(&SZL_ID_0037_IDX_XXXX,sizeof(SZL_ID_0037_IDX_XXXX));break;
|
|
case 0x0F37 : SZLData(&SZL_ID_0F37_IDX_XXXX,sizeof(SZL_ID_0F37_IDX_XXXX));break;
|
|
case 0x0074 : SZLData(&SZL_ID_0074_IDX_XXXX,sizeof(SZL_ID_0074_IDX_XXXX));break;
|
|
case 0x0F74 : SZLData(&SZL_ID_0F74_IDX_XXXX,sizeof(SZL_ID_0F74_IDX_XXXX));break;
|
|
case 0x0591 : SZLData(&SZL_ID_0591_IDX_XXXX,sizeof(SZL_ID_0591_IDX_XXXX));break;
|
|
case 0x0A91 : SZLData(&SZL_ID_0A91_IDX_XXXX,sizeof(SZL_ID_0A91_IDX_XXXX));break;
|
|
case 0x0F92 : SZLData(&SZL_ID_0F92_IDX_XXXX,sizeof(SZL_ID_0F92_IDX_XXXX));break;
|
|
case 0x0294 : SZLData(&SZL_ID_0294_IDX_XXXX,sizeof(SZL_ID_0294_IDX_XXXX));break;
|
|
case 0x0794 : SZLData(&SZL_ID_0794_IDX_XXXX,sizeof(SZL_ID_0794_IDX_XXXX));break;
|
|
case 0x0F94 : SZLData(&SZL_ID_0F94_IDX_XXXX,sizeof(SZL_ID_0F94_IDX_XXXX));break;
|
|
case 0x0095 : SZLData(&SZL_ID_0095_IDX_XXXX,sizeof(SZL_ID_0095_IDX_XXXX));break;
|
|
case 0x0F95 : SZLData(&SZL_ID_0F95_IDX_XXXX,sizeof(SZL_ID_0F95_IDX_XXXX));break;
|
|
case 0x00A0 : SZLData(&SZL_ID_00A0_IDX_XXXX,sizeof(SZL_ID_00A0_IDX_XXXX));break;
|
|
case 0x0FA0 : SZLData(&SZL_ID_0FA0_IDX_XXXX,sizeof(SZL_ID_0FA0_IDX_XXXX));break;
|
|
case 0x0017 : SZLData(&SZL_ID_0017_IDX_XXXX,sizeof(SZL_ID_0017_IDX_XXXX));break;
|
|
case 0x0F17 : SZLData(&SZL_ID_0F17_IDX_XXXX,sizeof(SZL_ID_0F17_IDX_XXXX));break;
|
|
case 0x0018 : SZLData(&SZL_ID_0018_IDX_XXXX,sizeof(SZL_ID_0018_IDX_XXXX));break;
|
|
case 0x0F18 : SZLData(&SZL_ID_0F18_IDX_XXXX,sizeof(SZL_ID_0F18_IDX_XXXX));break;
|
|
case 0x001A : SZLData(&SZL_ID_001A_IDX_XXXX,sizeof(SZL_ID_001A_IDX_XXXX));break;
|
|
case 0x0F1A : SZLData(&SZL_ID_0F1A_IDX_XXXX,sizeof(SZL_ID_0F1A_IDX_XXXX));break;
|
|
case 0x001B : SZLData(&SZL_ID_001B_IDX_XXXX,sizeof(SZL_ID_001B_IDX_XXXX));break;
|
|
case 0x0F1B : SZLData(&SZL_ID_0F1B_IDX_XXXX,sizeof(SZL_ID_0F1B_IDX_XXXX));break;
|
|
case 0x0021 : SZLData(&SZL_ID_0021_IDX_XXXX,sizeof(SZL_ID_0021_IDX_XXXX));break;
|
|
case 0x0A21 : SZLData(&SZL_ID_0A21_IDX_XXXX,sizeof(SZL_ID_0A21_IDX_XXXX));break;
|
|
case 0x0F21 : SZLData(&SZL_ID_0F21_IDX_XXXX,sizeof(SZL_ID_0F21_IDX_XXXX));break;
|
|
case 0x0023 : SZLData(&SZL_ID_0023_IDX_XXXX,sizeof(SZL_ID_0023_IDX_XXXX));break;
|
|
case 0x0F23 : SZLData(&SZL_ID_0F23_IDX_XXXX,sizeof(SZL_ID_0F23_IDX_XXXX));break;
|
|
case 0x0024 : SZLData(&SZL_ID_0024_IDX_XXXX,sizeof(SZL_ID_0024_IDX_XXXX));break;
|
|
case 0x0124 : SZLData(&SZL_ID_0124_IDX_XXXX,sizeof(SZL_ID_0124_IDX_XXXX));break;
|
|
case 0x0424 : SZL_ID424();break;
|
|
case 0x0038 : SZLData(&SZL_ID_0038_IDX_XXXX,sizeof(SZL_ID_0038_IDX_XXXX));break;
|
|
case 0x0F38 : SZLData(&SZL_ID_0F38_IDX_XXXX,sizeof(SZL_ID_0F38_IDX_XXXX));break;
|
|
case 0x003A : SZLData(&SZL_ID_003A_IDX_XXXX,sizeof(SZL_ID_003A_IDX_XXXX));break;
|
|
case 0x0F3A : SZLData(&SZL_ID_0F3A_IDX_XXXX,sizeof(SZL_ID_0F3A_IDX_XXXX));break;
|
|
case 0x0F9A : SZLData(&SZL_ID_0F9A_IDX_XXXX,sizeof(SZL_ID_0F9A_IDX_XXXX));break;
|
|
case 0x0D91 : switch(SZL.Index){
|
|
case 0x0000 : SZLData(&SZL_ID_0D91_IDX_0000,sizeof(SZL_ID_0D91_IDX_0000));break;
|
|
default: SZLNotAvailable();break;
|
|
};
|
|
break;
|
|
case 0x0092 : switch(SZL.Index){
|
|
case 0x0000 : SZLData(&SZL_ID_0092_IDX_0000,sizeof(SZL_ID_0092_IDX_0000));break;
|
|
default : SZLNotAvailable();break;
|
|
};break;
|
|
case 0x0292 : switch(SZL.Index){
|
|
case 0x0000 : SZLData(&SZL_ID_0292_IDX_0000,sizeof(SZL_ID_0292_IDX_0000));break;
|
|
default : SZLNotAvailable();break;
|
|
};break;
|
|
case 0x0692 : switch(SZL.Index){
|
|
case 0x0000 : SZLData(&SZL_ID_0692_IDX_0000,sizeof(SZL_ID_0692_IDX_0000));break;
|
|
default : SZLNotAvailable();break;
|
|
};break;
|
|
case 0x0094 : switch(SZL.Index){
|
|
case 0x0000 : SZLData(&SZL_ID_0094_IDX_0000,sizeof(SZL_ID_0094_IDX_0000));break;
|
|
default : SZLNotAvailable();break;
|
|
};break;
|
|
case 0x0D97 : switch(SZL.Index){
|
|
case 0x0000 : SZLData(&SZL_ID_0D97_IDX_0000,sizeof(SZL_ID_0D97_IDX_0000));break;
|
|
default : SZLNotAvailable();break;
|
|
};break;
|
|
case 0x0111 : switch(SZL.Index){
|
|
case 0x0001 : SZLData(&SZL_ID_0111_IDX_0001,sizeof(SZL_ID_0111_IDX_0001));break;
|
|
case 0x0006 : SZLData(&SZL_ID_0111_IDX_0006,sizeof(SZL_ID_0111_IDX_0006));break;
|
|
case 0x0007 : SZLData(&SZL_ID_0111_IDX_0007,sizeof(SZL_ID_0111_IDX_0007));break;
|
|
default : SZLNotAvailable();break;
|
|
};break;
|
|
case 0x0F11 : switch(SZL.Index){
|
|
case 0x0001 : SZLData(&SZL_ID_0F11_IDX_0001,sizeof(SZL_ID_0F11_IDX_0001));break;
|
|
case 0x0006 : SZLData(&SZL_ID_0F11_IDX_0006,sizeof(SZL_ID_0F11_IDX_0006));break;
|
|
case 0x0007 : SZLData(&SZL_ID_0F11_IDX_0007,sizeof(SZL_ID_0F11_IDX_0007));break;
|
|
default : SZLNotAvailable();break;
|
|
};break;
|
|
case 0x0112 : switch(SZL.Index){
|
|
case 0x0000 : SZLData(&SZL_ID_0112_IDX_0000,sizeof(SZL_ID_0112_IDX_0000));break;
|
|
case 0x0100 : SZLData(&SZL_ID_0112_IDX_0100,sizeof(SZL_ID_0112_IDX_0100));break;
|
|
case 0x0200 : SZLData(&SZL_ID_0112_IDX_0200,sizeof(SZL_ID_0112_IDX_0200));break;
|
|
case 0x0400 : SZLData(&SZL_ID_0112_IDX_0400,sizeof(SZL_ID_0112_IDX_0400));break;
|
|
default : SZLNotAvailable();break;
|
|
};break;
|
|
case 0x0F12 : switch(SZL.Index){
|
|
case 0x0000 : SZLData(&SZL_ID_0F12_IDX_0000,sizeof(SZL_ID_0F12_IDX_0000));break;
|
|
case 0x0100 : SZLData(&SZL_ID_0F12_IDX_0100,sizeof(SZL_ID_0F12_IDX_0100));break;
|
|
case 0x0200 : SZLData(&SZL_ID_0F12_IDX_0200,sizeof(SZL_ID_0F12_IDX_0200));break;
|
|
case 0x0400 : SZLData(&SZL_ID_0F12_IDX_0400,sizeof(SZL_ID_0F12_IDX_0400));break;
|
|
default : SZLNotAvailable();break;
|
|
};break;
|
|
case 0x0113 : switch(SZL.Index){
|
|
case 0x0001 : SZLData(&SZL_ID_0113_IDX_0001,sizeof(SZL_ID_0113_IDX_0001));break;
|
|
default : SZLNotAvailable();break;
|
|
};break;
|
|
case 0x0115 : switch(SZL.Index){
|
|
case 0x0800 : SZLData(&SZL_ID_0115_IDX_0800,sizeof(SZL_ID_0115_IDX_0800));break;
|
|
default : SZLNotAvailable();break;
|
|
};break;
|
|
case 0x011C : switch(SZL.Index){
|
|
case 0x0001 : SZLData(&SZL_ID_011C_IDX_0001,sizeof(SZL_ID_011C_IDX_0001));break;
|
|
case 0x0002 : SZLData(&SZL_ID_011C_IDX_0002,sizeof(SZL_ID_011C_IDX_0002));break;
|
|
case 0x0003 : SZLData(&SZL_ID_011C_IDX_0003,sizeof(SZL_ID_011C_IDX_0003));break;
|
|
case 0x0004 : SZLData(&SZL_ID_011C_IDX_0004,sizeof(SZL_ID_011C_IDX_0004));break;
|
|
case 0x0005 : SZLData(&SZL_ID_011C_IDX_0005,sizeof(SZL_ID_011C_IDX_0005));break;
|
|
case 0x0007 : SZLData(&SZL_ID_011C_IDX_0007,sizeof(SZL_ID_011C_IDX_0007));break;
|
|
case 0x0008 : SZLData(&SZL_ID_011C_IDX_0008,sizeof(SZL_ID_011C_IDX_0008));break;
|
|
case 0x0009 : SZLData(&SZL_ID_011C_IDX_0009,sizeof(SZL_ID_011C_IDX_0009));break;
|
|
case 0x000A : SZLData(&SZL_ID_011C_IDX_000A,sizeof(SZL_ID_011C_IDX_000A));break;
|
|
case 0x000B : SZLData(&SZL_ID_011C_IDX_000B,sizeof(SZL_ID_011C_IDX_000B));break;
|
|
default : SZLNotAvailable();break;
|
|
};break;
|
|
case 0x0222 : switch(SZL.Index){
|
|
case 0x0001 : SZLData(&SZL_ID_0222_IDX_0001,sizeof(SZL_ID_0222_IDX_0001));break;
|
|
case 0x000A : SZLData(&SZL_ID_0222_IDX_000A,sizeof(SZL_ID_0222_IDX_000A));break;
|
|
case 0x0014 : SZLData(&SZL_ID_0222_IDX_0014,sizeof(SZL_ID_0222_IDX_0014));break;
|
|
case 0x0028 : SZLData(&SZL_ID_0222_IDX_0028,sizeof(SZL_ID_0222_IDX_0028));break;
|
|
case 0x0050 : SZLData(&SZL_ID_0222_IDX_0050,sizeof(SZL_ID_0222_IDX_0050));break;
|
|
case 0x0064 : SZLData(&SZL_ID_0222_IDX_0064,sizeof(SZL_ID_0222_IDX_0064));break;
|
|
default : SZLNotAvailable();break;
|
|
};break;
|
|
case 0x0125 : switch(SZL.Index){
|
|
case 0x0000 : SZLData(&SZL_ID_0125_IDX_0000,sizeof(SZL_ID_0125_IDX_0000));break;
|
|
case 0x0001 : SZLData(&SZL_ID_0125_IDX_0001,sizeof(SZL_ID_0125_IDX_0001));break;
|
|
default : SZLNotAvailable();break;
|
|
};break;
|
|
case 0x0225 : switch(SZL.Index){
|
|
case 0x0001 : SZLData(&SZL_ID_0225_IDX_0001,sizeof(SZL_ID_0225_IDX_0001));break;
|
|
default : SZLNotAvailable();break;
|
|
};break;
|
|
case 0x0131 : switch(SZL.Index){
|
|
case 0x0001 : SZLData(&SZL_ID_0131_IDX_0001,sizeof(SZL_ID_0131_IDX_0001));break;
|
|
case 0x0002 : SZLData(&SZL_ID_0131_IDX_0002,sizeof(SZL_ID_0131_IDX_0002));break;
|
|
case 0x0003 : SZL_ID131_IDX003();break;
|
|
case 0x0004 : SZLData(&SZL_ID_0131_IDX_0004,sizeof(SZL_ID_0131_IDX_0004));break;
|
|
case 0x0005 : SZLData(&SZL_ID_0131_IDX_0005,sizeof(SZL_ID_0131_IDX_0005));break;
|
|
case 0x0006 : SZLData(&SZL_ID_0131_IDX_0006,sizeof(SZL_ID_0131_IDX_0006));break;
|
|
case 0x0007 : SZLData(&SZL_ID_0131_IDX_0007,sizeof(SZL_ID_0131_IDX_0007));break;
|
|
case 0x0008 : SZLData(&SZL_ID_0131_IDX_0008,sizeof(SZL_ID_0131_IDX_0008));break;
|
|
case 0x0009 : SZLData(&SZL_ID_0131_IDX_0009,sizeof(SZL_ID_0131_IDX_0009));break;
|
|
default : SZLNotAvailable();break;
|
|
};break;
|
|
case 0x0117 : switch(SZL.Index){
|
|
case 0x0000 : SZLData(&SZL_ID_0117_IDX_0000,sizeof(SZL_ID_0117_IDX_0000));break;
|
|
case 0x0001 : SZLData(&SZL_ID_0117_IDX_0001,sizeof(SZL_ID_0117_IDX_0001));break;
|
|
case 0x0002 : SZLData(&SZL_ID_0117_IDX_0002,sizeof(SZL_ID_0117_IDX_0002));break;
|
|
case 0x0003 : SZLData(&SZL_ID_0117_IDX_0003,sizeof(SZL_ID_0117_IDX_0003));break;
|
|
case 0x0004 : SZLData(&SZL_ID_0117_IDX_0004,sizeof(SZL_ID_0117_IDX_0004));break;
|
|
default : SZLNotAvailable();break;
|
|
};break;
|
|
case 0x0118 : switch(SZL.Index){
|
|
case 0x0000 : SZLData(&SZL_ID_0118_IDX_0000,sizeof(SZL_ID_0118_IDX_0000));break;
|
|
case 0x0001 : SZLData(&SZL_ID_0118_IDX_0001,sizeof(SZL_ID_0118_IDX_0001));break;
|
|
case 0x0002 : SZLData(&SZL_ID_0118_IDX_0002,sizeof(SZL_ID_0118_IDX_0002));break;
|
|
case 0x0003 : SZLData(&SZL_ID_0118_IDX_0003,sizeof(SZL_ID_0118_IDX_0003));break;
|
|
default : SZLNotAvailable();break;
|
|
};break;
|
|
case 0x0132 : switch(SZL.Index){
|
|
case 0x0001 : SZLData(&SZL_ID_0132_IDX_0001,sizeof(SZL_ID_0132_IDX_0001));break;
|
|
case 0x0002 : SZLData(&SZL_ID_0132_IDX_0002,sizeof(SZL_ID_0132_IDX_0002));break;
|
|
case 0x0003 : SZLData(&SZL_ID_0132_IDX_0003,sizeof(SZL_ID_0132_IDX_0003));break;
|
|
case 0x0004 : SZLData(&SZL_ID_0132_IDX_0004,sizeof(SZL_ID_0132_IDX_0004));break;
|
|
case 0x0005 : SZLData(&SZL_ID_0132_IDX_0005,sizeof(SZL_ID_0132_IDX_0005));break;
|
|
case 0x0006 : SZLData(&SZL_ID_0132_IDX_0006,sizeof(SZL_ID_0132_IDX_0006));break;
|
|
case 0x0007 : SZLData(&SZL_ID_0132_IDX_0007,sizeof(SZL_ID_0132_IDX_0007));break;
|
|
case 0x0008 : SZLData(&SZL_ID_0132_IDX_0008,sizeof(SZL_ID_0132_IDX_0008));break;
|
|
case 0x0009 : SZLData(&SZL_ID_0132_IDX_0009,sizeof(SZL_ID_0132_IDX_0009));break;
|
|
case 0x000A : SZLData(&SZL_ID_0132_IDX_000A,sizeof(SZL_ID_0132_IDX_000A));break;
|
|
case 0x000B : SZLData(&SZL_ID_0132_IDX_000B,sizeof(SZL_ID_0132_IDX_000B));break;
|
|
case 0x000C : SZLData(&SZL_ID_0132_IDX_000C,sizeof(SZL_ID_0132_IDX_000C));break;
|
|
default : SZLNotAvailable();break;
|
|
};break;
|
|
case 0x0137 : switch(SZL.Index){
|
|
case 0x07FE : SZLData(&SZL_ID_0137_IDX_07FE,sizeof(SZL_ID_0137_IDX_07FE));break;
|
|
default : SZLNotAvailable();break;
|
|
};break;
|
|
case 0x01A0 : switch(SZL.Index){
|
|
case 0x0000 : SZLData(&SZL_ID_01A0_IDX_0000,sizeof(SZL_ID_01A0_IDX_0000));break;
|
|
case 0x0001 : SZLData(&SZL_ID_01A0_IDX_0001,sizeof(SZL_ID_01A0_IDX_0001));break;
|
|
case 0x0002 : SZLData(&SZL_ID_01A0_IDX_0002,sizeof(SZL_ID_01A0_IDX_0002));break;
|
|
case 0x0003 : SZLData(&SZL_ID_01A0_IDX_0003,sizeof(SZL_ID_01A0_IDX_0003));break;
|
|
case 0x0004 : SZLData(&SZL_ID_01A0_IDX_0004,sizeof(SZL_ID_01A0_IDX_0004));break;
|
|
case 0x0005 : SZLData(&SZL_ID_01A0_IDX_0005,sizeof(SZL_ID_01A0_IDX_0005));break;
|
|
case 0x0006 : SZLData(&SZL_ID_01A0_IDX_0006,sizeof(SZL_ID_01A0_IDX_0006));break;
|
|
case 0x0007 : SZLData(&SZL_ID_01A0_IDX_0007,sizeof(SZL_ID_01A0_IDX_0007));break;
|
|
case 0x0008 : SZLData(&SZL_ID_01A0_IDX_0008,sizeof(SZL_ID_01A0_IDX_0008));break;
|
|
case 0x0009 : SZLData(&SZL_ID_01A0_IDX_0009,sizeof(SZL_ID_01A0_IDX_0009));break;
|
|
case 0x000A : SZLData(&SZL_ID_01A0_IDX_000A,sizeof(SZL_ID_01A0_IDX_000A));break;
|
|
case 0x000B : SZLData(&SZL_ID_01A0_IDX_000B,sizeof(SZL_ID_01A0_IDX_000B));break;
|
|
case 0x000C : SZLData(&SZL_ID_01A0_IDX_000C,sizeof(SZL_ID_01A0_IDX_000C));break;
|
|
case 0x000D : SZLData(&SZL_ID_01A0_IDX_000D,sizeof(SZL_ID_01A0_IDX_000D));break;
|
|
case 0x000E : SZLData(&SZL_ID_01A0_IDX_000E,sizeof(SZL_ID_01A0_IDX_000E));break;
|
|
case 0x000F : SZLData(&SZL_ID_01A0_IDX_000F,sizeof(SZL_ID_01A0_IDX_000F));break;
|
|
case 0x0010 : SZLData(&SZL_ID_01A0_IDX_0010,sizeof(SZL_ID_01A0_IDX_0010));break;
|
|
case 0x0011 : SZLData(&SZL_ID_01A0_IDX_0011,sizeof(SZL_ID_01A0_IDX_0011));break;
|
|
case 0x0012 : SZLData(&SZL_ID_01A0_IDX_0012,sizeof(SZL_ID_01A0_IDX_0012));break;
|
|
case 0x0013 : SZLData(&SZL_ID_01A0_IDX_0013,sizeof(SZL_ID_01A0_IDX_0013));break;
|
|
case 0x0014 : SZLData(&SZL_ID_01A0_IDX_0014,sizeof(SZL_ID_01A0_IDX_0014));break;
|
|
case 0x0015 : SZLData(&SZL_ID_01A0_IDX_0015,sizeof(SZL_ID_01A0_IDX_0015));break;
|
|
default : SZLNotAvailable();break;
|
|
};break;
|
|
case 0x0174 : switch(SZL.Index){
|
|
case 0x0001 : SZLData(&SZL_ID_0174_IDX_0001,sizeof(SZL_ID_0174_IDX_0001));break;
|
|
case 0x0004 : SZLData(&SZL_ID_0174_IDX_0004,sizeof(SZL_ID_0174_IDX_0004));break;
|
|
case 0x0005 : SZLData(&SZL_ID_0174_IDX_0005,sizeof(SZL_ID_0174_IDX_0005));break;
|
|
case 0x0006 : SZLData(&SZL_ID_0174_IDX_0006,sizeof(SZL_ID_0174_IDX_0006));break;
|
|
case 0x000B : SZLData(&SZL_ID_0174_IDX_000B,sizeof(SZL_ID_0174_IDX_000B));break;
|
|
case 0x000C : SZLData(&SZL_ID_0174_IDX_000C,sizeof(SZL_ID_0174_IDX_000C));break;
|
|
default : SZLNotAvailable();break;
|
|
};break;
|
|
case 0x0194 : switch(SZL.Index){
|
|
case 0x0064 : SZLData(&SZL_ID_0194_IDX_0064,sizeof(SZL_ID_0194_IDX_0064));break;
|
|
default : SZLNotAvailable();break;
|
|
};break;
|
|
case 0x0694 : switch(SZL.Index){
|
|
case 0x0064 : SZLData(&SZL_ID_0694_IDX_0064,sizeof(SZL_ID_0694_IDX_0064));break;
|
|
default : SZLNotAvailable();break;
|
|
};break;
|
|
case 0x0232 : switch(SZL.Index){
|
|
case 0x0001 : SZLData(&SZL_ID_0232_IDX_0001,sizeof(SZL_ID_0232_IDX_0001));break;
|
|
case 0x0004 : SZLData(&SZL_ID_0232_IDX_0004,sizeof(SZL_ID_0232_IDX_0004));break;
|
|
default : SZLNotAvailable();break;
|
|
};break;
|
|
case 0x0C91 : switch(SZL.Index){
|
|
case 0x07FE : SZLData(&SZL_ID_0C91_IDX_07FE,sizeof(SZL_ID_0C91_IDX_07FE));break;
|
|
default : SZLNotAvailable();break;
|
|
};break;
|
|
default : SZLNotAvailable();break;
|
|
}
|
|
// Event
|
|
if (SZL.SZLDone)
|
|
DoEvent(evcReadSZL,evrNoError,SZL.ID,SZL.Index,0,0);
|
|
else
|
|
DoEvent(evcReadSZL,evrInvalidSZL,SZL.ID,SZL.Index,0,0);
|
|
return true;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
bool TS7Worker::PerformGroupSecurity()
|
|
{
|
|
PReqFunSecurity ReqParams;
|
|
PResParamsSecurity ResParams;
|
|
PResDataSecurity ResData;
|
|
TS7Answer17 Answer;
|
|
int TotalSize;
|
|
|
|
ReqParams=PReqFunSecurity(pbyte(PDUH_in)+ReqHeaderSize);
|
|
ResParams=PResParamsSecurity(pbyte(&Answer)+ResHeaderSize17);
|
|
ResData =PResDataSecurity(pbyte(ResParams)+sizeof(TResParamsSecurity));
|
|
|
|
// Prepares the answer
|
|
Answer.Header.P=0x32;
|
|
Answer.Header.PDUType=PduType_userdata;
|
|
Answer.Header.AB_EX=0x0000;
|
|
Answer.Header.Sequence=PDUH_in->Sequence;
|
|
Answer.Header.ParLen =SwapWord(sizeof(TResParamsSecurity));
|
|
Answer.Header.DataLen=SwapWord(0x0004);
|
|
// Params
|
|
ResParams->Head[0]=ReqParams->Head[0];
|
|
ResParams->Head[1]=ReqParams->Head[1];
|
|
ResParams->Head[2]=ReqParams->Head[2];
|
|
ResParams->Plen =0x08;
|
|
ResParams->Uk =0x12;
|
|
ResParams->Tg =0x85; // Type response, group functions info
|
|
ResParams->SubFun=ReqParams->SubFun;
|
|
ResParams->Seq =ReqParams->Seq;
|
|
ResParams->resvd =0x0000;
|
|
ResParams->Err =0x0000;
|
|
// Data
|
|
ResData->Ret =0x0A;
|
|
ResData->TS =0x00;
|
|
ResData->DLen=0x0000;
|
|
|
|
TotalSize=26;
|
|
isoSendBuffer(&Answer,TotalSize);
|
|
|
|
switch (ReqParams->SubFun)
|
|
{
|
|
case SFun_EnterPwd : DoEvent(evcSecurity,evrNoError,evsSetPassword,0,0,0); break;
|
|
case SFun_CancelPwd : DoEvent(evcSecurity,evrNoError,evsClrPassword,0,0,0); break;
|
|
default : DoEvent(evcSecurity,evrNoError,evsUnknown,0,0,0);
|
|
};
|
|
|
|
return true;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
bool TS7Worker::PerformGetClock()
|
|
{
|
|
PS7ReqParams7 ReqParams;
|
|
PS7ResParams7 ResParams;
|
|
TS7Answer17 Answer;
|
|
PResDataGetTime Data;
|
|
PS7Time PTime;
|
|
int TotalSize;
|
|
|
|
ReqParams=PS7ReqParams7(pbyte(PDUH_in)+ReqHeaderSize);
|
|
ResParams=PS7ResParams7(pbyte(&Answer)+ResHeaderSize17);
|
|
Data =PResDataGetTime(pbyte(&Answer)+ResHeaderSize17+sizeof(TS7Params7));
|
|
PTime =PS7Time(pbyte(Data)+6);
|
|
|
|
// Prepares the answer
|
|
Answer.Header.P=0x32;
|
|
Answer.Header.PDUType=PduType_userdata;
|
|
Answer.Header.AB_EX=0x0000;
|
|
Answer.Header.Sequence=PDUH_in->Sequence;
|
|
Answer.Header.ParLen =SwapWord(sizeof(TS7Params7));
|
|
Answer.Header.DataLen=SwapWord(sizeof(TResDataGetTime));
|
|
|
|
ResParams->Head[0]=ReqParams->Head[0];
|
|
ResParams->Head[1]=ReqParams->Head[1];
|
|
ResParams->Head[2]=ReqParams->Head[2];
|
|
ResParams->Plen =0x08;
|
|
ResParams->Uk =0x12;
|
|
ResParams->Tg =0x87; // Type response, group functions info
|
|
ResParams->SubFun=ReqParams->SubFun;
|
|
ResParams->Seq =ReqParams->Seq;
|
|
ResParams->resvd =0x0000;
|
|
ResParams->Err =0x0000;
|
|
|
|
Data->RetVal =0xFF;
|
|
Data->TSize =TS_ResOctet;
|
|
Data->Length =SwapWord(10);
|
|
Data->Rsvd =0x00;
|
|
Data->HiYear =0x20; // Year 2000 +
|
|
FillTime(PTime);
|
|
|
|
TotalSize=36;
|
|
isoSendBuffer(&Answer,TotalSize);
|
|
DoEvent(evcClock,evrNoError,evsGetClock,0,0,0);
|
|
return true;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
bool TS7Worker::PerformSetClock()
|
|
{
|
|
PS7ReqParams7 ReqParams;
|
|
PS7ResParams7 ResParams;
|
|
PResDataSetTime Data;
|
|
TS7Answer17 Answer;
|
|
int TotalSize;
|
|
|
|
ReqParams=PS7ReqParams7(pbyte(PDUH_in)+ReqHeaderSize);
|
|
ResParams=PS7ResParams7(pbyte(&Answer)+ResHeaderSize17);
|
|
Data =PResDataSetTime(pbyte(&Answer)+ResHeaderSize17+sizeof(TS7Params7));
|
|
|
|
// Prepares the answer
|
|
Answer.Header.P=0x32;
|
|
Answer.Header.PDUType=PduType_userdata;
|
|
Answer.Header.AB_EX=0x0000;
|
|
Answer.Header.Sequence=PDUH_in->Sequence;
|
|
Answer.Header.ParLen =SwapWord(sizeof(TS7Params7));
|
|
Answer.Header.DataLen=SwapWord(sizeof(TResDataSetTime));
|
|
|
|
ResParams->Head[0]=ReqParams->Head[0];
|
|
ResParams->Head[1]=ReqParams->Head[1];
|
|
ResParams->Head[2]=ReqParams->Head[2];
|
|
ResParams->Plen =0x08;
|
|
ResParams->Uk =0x12;
|
|
ResParams->Tg =0x87; // Type response, group functions info
|
|
ResParams->SubFun=ReqParams->SubFun;
|
|
ResParams->Seq =ReqParams->Seq;
|
|
ResParams->resvd =0x0000;
|
|
ResParams->Err =0x0000;
|
|
|
|
Data->RetVal =0x0A;
|
|
Data->TSize =0x00;
|
|
Data->Length =0x0000;
|
|
|
|
TotalSize=26;
|
|
isoSendBuffer(&Answer,TotalSize);
|
|
DoEvent(evcClock,evrNoError,evsSetClock,0,0,0);
|
|
return true;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
// S7 SERVER CLASS
|
|
//------------------------------------------------------------------------------
|
|
TSnap7Server::TSnap7Server()
|
|
{
|
|
CSRWHook = new TSnapCriticalSection();
|
|
OnReadEvent=NULL;
|
|
memset(&DB,0,sizeof(DB));
|
|
memset(&HA,0,sizeof(HA));
|
|
DBCount=0;
|
|
DBLimit=0;
|
|
ForcePDU = 0;
|
|
ResourceLess = false;
|
|
LocalPort=isoTcpPort;
|
|
CpuStatus=S7CpuStatusRun;
|
|
WorkInterval=100;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
TSnap7Server::~TSnap7Server()
|
|
{
|
|
DisposeAll();
|
|
delete CSRWHook;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
PWorkerSocket TSnap7Server::CreateWorkerSocket(socket_t Sock)
|
|
{
|
|
PWorkerSocket Result;
|
|
Result = new TS7Worker();
|
|
Result->SetSocket(Sock);
|
|
PS7Worker(Result)->FServer=this;
|
|
return Result;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
PS7Area TSnap7Server::FindDB(word DBNumber)
|
|
{
|
|
int c;
|
|
int max=DBLimit+1;
|
|
|
|
for (c=0; c<max; c++)
|
|
{
|
|
if (DB[c]!=NULL)
|
|
{
|
|
if (DB[c]->Number==DBNumber)
|
|
{
|
|
return DB[c];
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
int TSnap7Server::IndexOfDB(word DBNumber)
|
|
{
|
|
int c;
|
|
int max=DBLimit+1;
|
|
|
|
for (c=0; c<max; c++)
|
|
{
|
|
if (DB[c]!=NULL)
|
|
{
|
|
if (DB[c]->Number==DBNumber)
|
|
{
|
|
return c;
|
|
}
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
int TSnap7Server::FindFirstFreeDB()
|
|
{
|
|
int c;
|
|
for (c=0; c < MaxDB; c++)
|
|
{
|
|
if (DB[c]==NULL)
|
|
return c;
|
|
}
|
|
return -1;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
int TSnap7Server::RegisterDB(word Number, void *pUsrData, word Size)
|
|
{
|
|
PS7Area TheArea;
|
|
int index;
|
|
|
|
if (pUsrData==0)
|
|
return errSrvDBNullPointer;
|
|
|
|
if (FindDB(Number)!=NULL)
|
|
return errSrvAreaAlreadyExists;
|
|
|
|
index=FindFirstFreeDB();
|
|
if (index==-1)
|
|
return errSrvTooManyDB;
|
|
|
|
TheArea =new TS7Area;
|
|
TheArea->Number=Number;
|
|
TheArea->cs=new TSnapCriticalSection();
|
|
TheArea->PData=pbyte(pUsrData);
|
|
TheArea->Size=Size;
|
|
DB[index]=TheArea;
|
|
DBCount++;
|
|
if (DBLimit<index)
|
|
DBLimit=index;
|
|
return 0;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
void TSnap7Server::DisposeAll()
|
|
{
|
|
PS7Area TheDB;
|
|
int c;
|
|
// Unregister DBs
|
|
for (c = 0; c < MaxDB; c++)
|
|
{
|
|
if (DB[c]!=NULL)
|
|
{
|
|
// Unregister should be done with the server in stop mode
|
|
// however we can minimize the risk...
|
|
TheDB=DB[c];
|
|
DB[c]=NULL;
|
|
if (TheDB->cs!=0)
|
|
delete TheDB->cs;
|
|
delete TheDB;
|
|
}
|
|
}
|
|
DBCount=0;
|
|
// Unregister other
|
|
for (c = srvAreaPE; c < srvAreaDB; c++)
|
|
UnregisterSys(c);
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
int TSnap7Server::RegisterSys(int AreaCode, void *pUsrData, word Size)
|
|
{
|
|
PS7Area TheArea;
|
|
|
|
if (pUsrData==0)
|
|
return errSrvDBNullPointer;
|
|
|
|
if ((AreaCode<srvAreaPE) || (AreaCode>srvAreaTM))
|
|
return errSrvUnknownArea;
|
|
|
|
if (HA[AreaCode]==0)
|
|
{
|
|
TheArea=new TS7Area;
|
|
TheArea->cs=new TSnapCriticalSection();
|
|
TheArea->PData=pbyte(pUsrData);
|
|
TheArea->Size=Size;
|
|
HA[AreaCode]=TheArea;
|
|
return 0;
|
|
}
|
|
else
|
|
return errSrvAreaAlreadyExists;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
int TSnap7Server::UnregisterDB(word DBNumber)
|
|
{
|
|
PS7Area TheDB;
|
|
int index = IndexOfDB(DBNumber);
|
|
if (index==-1)
|
|
return errSrvInvalidParams;
|
|
|
|
// Unregister should be done with the server in stop mode
|
|
// however we can minimize the risk...
|
|
TheDB=DB[index];
|
|
DB[index]=NULL;
|
|
if (TheDB->cs!=NULL)
|
|
delete TheDB->cs;
|
|
delete TheDB;
|
|
DBCount--;
|
|
|
|
return 0;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
int TSnap7Server::UnregisterSys(int AreaCode)
|
|
{
|
|
PS7Area TheArea;
|
|
if (HA[AreaCode]!=NULL)
|
|
{
|
|
// Unregister should be done with the server in stop mode
|
|
// however we can minimize the risk...
|
|
TheArea=HA[AreaCode];
|
|
HA[AreaCode]=NULL;
|
|
if (TheArea->cs!=NULL)
|
|
delete TheArea->cs;
|
|
delete TheArea;
|
|
}
|
|
return 0;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
int TSnap7Server::StartTo(const char *Address)
|
|
{
|
|
return TCustomMsgServer::StartTo(Address, LocalPort);
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
int TSnap7Server::GetParam(int ParamNumber, void *pValue)
|
|
{
|
|
switch (ParamNumber)
|
|
{
|
|
case p_u16_LocalPort:
|
|
*Puint16_t(pValue)=LocalPort;
|
|
break;
|
|
case p_i32_WorkInterval:
|
|
*Pint32_t(pValue)=WorkInterval;
|
|
break;
|
|
case p_i32_MaxClients:
|
|
*Pint32_t(pValue)=MaxClients;
|
|
break;
|
|
case p_i32_PDURequest:
|
|
*Pint32_t(pValue) = ForcePDU;
|
|
break;
|
|
default: return errSrvInvalidParamNumber;
|
|
}
|
|
return 0;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
int TSnap7Server::SetParam(int ParamNumber, void *pValue)
|
|
{
|
|
switch (ParamNumber)
|
|
{
|
|
case p_u16_LocalPort:
|
|
if (Status == SrvStopped)
|
|
LocalPort = *Puint16_t(pValue);
|
|
else
|
|
return errSrvCannotChangeParam;
|
|
break;
|
|
case p_i32_PDURequest:
|
|
if (Status == SrvStopped)
|
|
{
|
|
int PDU = *Pint32_t(pValue);
|
|
if (PDU == 0)
|
|
ForcePDU = 0; // ForcePDU=0 --> The server accepts the client's proposal
|
|
else
|
|
{
|
|
if ((PDU < MinPduSize) || (PDU>IsoPayload_Size))
|
|
return errSrvInvalidParams; // Wrong value
|
|
else
|
|
ForcePDU = PDU; // The server imposes ForcePDU as PDU size
|
|
}
|
|
}
|
|
else
|
|
return errSrvCannotChangeParam;
|
|
break;
|
|
case p_i32_WorkInterval:
|
|
WorkInterval=*Pint32_t(pValue);
|
|
break;
|
|
case p_i32_MaxClients:
|
|
if (ClientsCount==0 && Status==SrvStopped)
|
|
MaxClients=*Pint32_t(pValue);
|
|
else
|
|
return errSrvCannotChangeParam;
|
|
break;
|
|
default: return errSrvInvalidParamNumber;
|
|
}
|
|
return 0;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
int TSnap7Server::RegisterArea(int AreaCode, word Index, void *pUsrData, word Size)
|
|
{
|
|
if (AreaCode==srvAreaDB)
|
|
return RegisterDB(Index, pUsrData, Size);
|
|
else
|
|
return RegisterSys(AreaCode,pUsrData, Size);
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
int TSnap7Server::UnregisterArea(int AreaCode, word Index)
|
|
{
|
|
if (AreaCode==srvAreaDB)
|
|
return UnregisterDB(Index);
|
|
else
|
|
if ((AreaCode>=srvAreaPE) && (AreaCode<=srvAreaTM))
|
|
return UnregisterSys(AreaCode);
|
|
else
|
|
return errSrvInvalidParams;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
int TSnap7Server::LockArea(int AreaCode, word DBNumber)
|
|
{
|
|
int index;
|
|
|
|
if ((AreaCode>=srvAreaPE) && (AreaCode<=srvAreaTM))
|
|
{
|
|
if (HA[AreaCode]!=0)
|
|
{
|
|
HA[AreaCode]->cs->Enter();
|
|
return 0;
|
|
}
|
|
else
|
|
return errSrvInvalidParams;
|
|
}
|
|
else
|
|
if (AreaCode==srvAreaDB)
|
|
{
|
|
index=IndexOfDB(DBNumber);
|
|
if (index!=-1)
|
|
{
|
|
DB[index]->cs->Enter();
|
|
return 0;
|
|
}
|
|
else
|
|
return errSrvInvalidParams;
|
|
}
|
|
else
|
|
return errSrvInvalidParams;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
int TSnap7Server::UnlockArea(int AreaCode, word DBNumber)
|
|
{
|
|
int index;
|
|
|
|
if ((AreaCode>=srvAreaPE) && (AreaCode<=srvAreaTM))
|
|
{
|
|
if (HA[AreaCode]!=0)
|
|
{
|
|
HA[AreaCode]->cs->Leave();
|
|
return 0;
|
|
}
|
|
else
|
|
return errSrvInvalidParams;
|
|
}
|
|
else
|
|
if (AreaCode==srvAreaDB)
|
|
{
|
|
index=IndexOfDB(DBNumber);
|
|
if (index!=-1)
|
|
{
|
|
DB[index]->cs->Leave();
|
|
return 0;
|
|
}
|
|
else
|
|
return errSrvInvalidParams;
|
|
}
|
|
else
|
|
return errSrvInvalidParams;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
int TSnap7Server::SetReadEventsCallBack(pfn_SrvCallBack PCallBack, void *UsrPtr)
|
|
{
|
|
OnReadEvent = PCallBack;
|
|
FReadUsrPtr = UsrPtr;
|
|
return 0;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
int TSnap7Server::SetRWAreaCallBack(pfn_RWAreaCallBack PCallBack, void *UsrPtr)
|
|
{
|
|
OnRWArea = PCallBack;
|
|
FRWAreaUsrPtr = UsrPtr;
|
|
ResourceLess = OnRWArea != NULL;
|
|
return 0;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
void TSnap7Server::DoReadEvent(int Sender, longword Code, word RetCode, word Param1,
|
|
word Param2, word Param3, word Param4)
|
|
{
|
|
TSrvEvent SrvReadEvent;
|
|
if (!Destroying && (OnReadEvent != NULL))
|
|
{
|
|
CSEvent->Enter();
|
|
|
|
time(&SrvReadEvent.EvtTime);
|
|
SrvReadEvent.EvtSender = Sender;
|
|
SrvReadEvent.EvtCode = Code;
|
|
SrvReadEvent.EvtRetCode = RetCode;
|
|
SrvReadEvent.EvtParam1 = Param1;
|
|
SrvReadEvent.EvtParam2 = Param2;
|
|
SrvReadEvent.EvtParam3 = Param3;
|
|
SrvReadEvent.EvtParam4 = Param4;
|
|
|
|
try
|
|
{ // callback is outside here, we have to shield it
|
|
OnReadEvent(FReadUsrPtr, &SrvReadEvent, sizeof (TSrvEvent));
|
|
} catch (...)
|
|
{
|
|
};
|
|
CSEvent->Leave();
|
|
};
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
bool TSnap7Server::DoReadArea(int Sender, int Area, int DBNumber, int Start, int Size, int WordLen, void *pUsrData)
|
|
{
|
|
TS7Tag Tag;
|
|
bool Result = false;
|
|
if (!Destroying && (OnRWArea != NULL))
|
|
{
|
|
CSRWHook->Enter();
|
|
try
|
|
{
|
|
Tag.Area = Area;
|
|
Tag.DBNumber = DBNumber;
|
|
Tag.Start = Start;
|
|
Tag.Size = Size;
|
|
Tag.WordLen = WordLen;
|
|
// callback is outside here, we have to shield it
|
|
Result = OnRWArea(FRWAreaUsrPtr, Sender, OperationRead, &Tag, pUsrData) == 0;
|
|
}
|
|
catch (...)
|
|
{
|
|
Result = false;
|
|
};
|
|
CSRWHook->Leave();
|
|
}
|
|
return Result;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
bool TSnap7Server::DoWriteArea(int Sender, int Area, int DBNumber, int Start, int Size, int WordLen, void *pUsrData)
|
|
{
|
|
TS7Tag Tag;
|
|
bool Result = false;
|
|
if (!Destroying && (OnRWArea != NULL))
|
|
{
|
|
CSRWHook->Enter();
|
|
try
|
|
{
|
|
Tag.Area = Area;
|
|
Tag.DBNumber = DBNumber;
|
|
Tag.Start = Start;
|
|
Tag.Size = Size;
|
|
Tag.WordLen = WordLen;
|
|
// callback is outside here, we have to shield it
|
|
Result = OnRWArea(FRWAreaUsrPtr, Sender, OperationWrite, &Tag, pUsrData) == 0;
|
|
}
|
|
catch (...)
|
|
{
|
|
Result = false;
|
|
};
|
|
CSRWHook->Leave();
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
|