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

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;
}