/*=============================================================================| | 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 ((c1) 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 (ReqLenPDULength = 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 ((cntDB[DBCnt]!=NULL) { Data->Items[cnt].BlockNum=SwapWord(FServer->DB[DBCnt]->Number); Data->Items[cnt].Unknown =0x22; Data->Items[cnt].BlockLang=0x05; cnt++; }; }; if ((cntRsvd=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; cNumber==DBNumber) { return DB[c]; } } } return NULL; } //------------------------------------------------------------------------------ int TSnap7Server::IndexOfDB(word DBNumber) { int c; int max=DBLimit+1; for (c=0; cNumber==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 (DBLimitcs!=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 ((AreaCodesrvAreaTM)) 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; }