From 4570809599fd168b2f8687fdeaf91d9af22917c1 Mon Sep 17 00:00:00 2001 From: Dave Nardella Date: Sat, 15 Mar 2025 12:18:02 +0100 Subject: [PATCH] Add files via upload --- utils/snap7_src/src/core/s7_client.cpp | 503 +++ utils/snap7_src/src/core/s7_client.h | 104 + utils/snap7_src/src/core/s7_firmware.h | 1256 +++++++ utils/snap7_src/src/core/s7_isotcp.cpp | 541 +++ utils/snap7_src/src/core/s7_isotcp.h | 271 ++ utils/snap7_src/src/core/s7_micro_client.cpp | 3328 ++++++++++++++++++ utils/snap7_src/src/core/s7_micro_client.h | 364 ++ utils/snap7_src/src/core/s7_partner.cpp | 1178 +++++++ utils/snap7_src/src/core/s7_partner.h | 284 ++ utils/snap7_src/src/core/s7_peer.cpp | 122 + utils/snap7_src/src/core/s7_peer.h | 58 + utils/snap7_src/src/core/s7_server.cpp | 2158 ++++++++++++ utils/snap7_src/src/core/s7_server.h | 261 ++ utils/snap7_src/src/core/s7_text.cpp | 788 +++++ utils/snap7_src/src/core/s7_text.h | 49 + utils/snap7_src/src/core/s7_types.h | 1066 ++++++ 16 files changed, 12331 insertions(+) create mode 100644 utils/snap7_src/src/core/s7_client.cpp create mode 100644 utils/snap7_src/src/core/s7_client.h create mode 100644 utils/snap7_src/src/core/s7_firmware.h create mode 100644 utils/snap7_src/src/core/s7_isotcp.cpp create mode 100644 utils/snap7_src/src/core/s7_isotcp.h create mode 100644 utils/snap7_src/src/core/s7_micro_client.cpp create mode 100644 utils/snap7_src/src/core/s7_micro_client.h create mode 100644 utils/snap7_src/src/core/s7_partner.cpp create mode 100644 utils/snap7_src/src/core/s7_partner.h create mode 100644 utils/snap7_src/src/core/s7_peer.cpp create mode 100644 utils/snap7_src/src/core/s7_peer.h create mode 100644 utils/snap7_src/src/core/s7_server.cpp create mode 100644 utils/snap7_src/src/core/s7_server.h create mode 100644 utils/snap7_src/src/core/s7_text.cpp create mode 100644 utils/snap7_src/src/core/s7_text.h create mode 100644 utils/snap7_src/src/core/s7_types.h diff --git a/utils/snap7_src/src/core/s7_client.cpp b/utils/snap7_src/src/core/s7_client.cpp new file mode 100644 index 0000000..c67e50c --- /dev/null +++ b/utils/snap7_src/src/core/s7_client.cpp @@ -0,0 +1,503 @@ +/*=============================================================================| +| PROJECT SNAP7 1.3.0 | +|==============================================================================| +| Copyright (C) 2013, 2015 Davide Nardella | +| All rights reserved. | +|==============================================================================| +| SNAP7 is free software: you can redistribute it and/or modify | +| it under the terms of the Lesser GNU General Public License as published by | +| the Free Software Foundation, either version 3 of the License, or | +| (at your option) any later version. | +| | +| It means that you can distribute your commercial software linked with | +| SNAP7 without the requirement to distribute the source code of your | +| application and without the requirement that your application be itself | +| distributed under LGPL. | +| | +| SNAP7 is distributed in the hope that it will be useful, | +| but WITHOUT ANY WARRANTY; without even the implied warranty of | +| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | +| Lesser GNU General Public License for more details. | +| | +| You should have received a copy of the GNU General Public License and a | +| copy of Lesser GNU General Public License along with Snap7. | +| If not, see http://www.gnu.org/licenses/ | +|=============================================================================*/ +#include "s7_client.h" + +//--------------------------------------------------------------------------- +TSnap7Client::TSnap7Client() +{ + FThread = 0; + CliCompletion = 0; + EvtJob = NULL; + EvtComplete = NULL; + FThread=NULL; + ThreadCreated = false; +} +//--------------------------------------------------------------------------- +TSnap7Client::~TSnap7Client() +{ + Destroying=true; + Disconnect(); + CliCompletion=NULL; + if (ThreadCreated) + { + CloseThread(); + delete EvtComplete; + delete EvtJob; + ThreadCreated=false; + } +} +//--------------------------------------------------------------------------- +void TSnap7Client::CloseThread() +{ + int Timeout; + + if (FThread) + { + FThread->Terminate(); + if (Job.Pending) + Timeout=3000; + else + Timeout=1000; + EvtJob->Set(); + if (FThread->WaitFor(Timeout)!=WAIT_OBJECT_0) + FThread->Kill(); + try { + delete FThread; + } + catch (...){ + } + + FThread=0; + } +} +//--------------------------------------------------------------------------- +void TSnap7Client::OpenThread() +{ + FThread = new TClientThread(this); + FThread->Start(); +} +//--------------------------------------------------------------------------- +int TSnap7Client::Reset(bool DoReconnect) +{ + bool WasConnected = Connected; + if (ThreadCreated) + { + CloseThread(); + Disconnect(); + OpenThread(); + } + else + Disconnect(); + + if (DoReconnect || WasConnected) + return Connect(); + else + return 0; +} +//--------------------------------------------------------------------------- +void TSnap7Client::DoCompletion() +{ + if ((CliCompletion!=NULL) && !Destroying) + { + try{ + CliCompletion(FUsrPtr, Job.Op, Job.Result); + }catch (...) + { + } + } +} +//--------------------------------------------------------------------------- +int TSnap7Client::SetAsCallback(pfn_CliCompletion pCompletion, void * usrPtr) +{ + CliCompletion=pCompletion; + FUsrPtr=usrPtr; + return 0; +} +//--------------------------------------------------------------------------- +int TSnap7Client::GetParam(int ParamNumber, void * pValue) +{ + // Actually there are no specific client params, maybe in future... + return TSnap7MicroClient::GetParam(ParamNumber, pValue); +} +//--------------------------------------------------------------------------- +int TSnap7Client::SetParam(int ParamNumber, void * pValue) +{ + // Actually there are no specific client params, maybe in future... + return TSnap7MicroClient::SetParam(ParamNumber, pValue); +} +//--------------------------------------------------------------------------- +bool TSnap7Client::CheckAsCompletion(int &opResult) +{ + if (!Job.Pending) + opResult=Job.Result; + else + if (!Destroying) + opResult=errCliJobPending; // don't set LastError here + else + { + opResult=errCliDestroying; + return true; + } + + return !Job.Pending; +} +//--------------------------------------------------------------------------- +int TSnap7Client::AsReadArea(int Area, int DBNumber, int Start, int Amount, int WordLen, void * pUsrData) +{ + if (!Job.Pending) + { + Job.Pending = true; + Job.Op = s7opReadArea; + Job.Area = Area; + Job.Number = DBNumber; + Job.Start = Start; + Job.Amount = Amount; + Job.WordLen = WordLen; + Job.pData = pUsrData; + JobStart = SysGetTick(); + StartAsyncJob(); + return 0; + } + else + return SetError(errCliJobPending); +} +//--------------------------------------------------------------------------- +int TSnap7Client::AsWriteArea(int Area, int DBNumber, int Start, int Amount, int WordLen, void * pUsrData) +{ + int ByteSize, TotalSize; + + if (!Job.Pending) + { + Job.Pending =true; + Job.Op =s7opWriteArea; + Job.Area =Area; + Job.Number =DBNumber; + Job.Start =Start; + // Performs some check first to copy the data + ByteSize=DataSizeByte(WordLen); + TotalSize=ByteSize*Amount; // Total size in bytes + if (ByteSize==0) + return SetError(errCliInvalidWordLen); + if ((TotalSize < 1) || (TotalSize > int(sizeof(opData)))) + return SetError(errCliInvalidParams); + Job.Amount =Amount; + Job.WordLen =WordLen; + // Doublebuffering + memcpy(&opData, pUsrData, TotalSize); + Job.pData =&opData; + JobStart =SysGetTick(); + StartAsyncJob(); + return 0; + } + else + return SetError(errCliJobPending); +} +//--------------------------------------------------------------------------- +int TSnap7Client::AsListBlocksOfType(int BlockType, PS7BlocksOfType pUsrData, int & ItemsCount) +{ + if (!Job.Pending) + { + Job.Pending =true; + Job.Op =s7opListBlocksOfType; + Job.Area =BlockType; + Job.pData =pUsrData; + Job.pAmount =&ItemsCount; + JobStart =SysGetTick(); + StartAsyncJob(); + return 0; + } + else + return SetError(errCliJobPending); +} +//--------------------------------------------------------------------------- +int TSnap7Client::AsReadSZL(int ID, int Index, PS7SZL pUsrData, int & Size) +{ + if (!Job.Pending) + { + Job.Pending =true; + Job.Op =s7opReadSZL; + Job.ID =ID; + Job.Index =Index; + Job.pData =pUsrData; + Job.pAmount =&Size; + Job.Amount =Size; + Job.IParam =1; // Data has to be copied into user buffer + JobStart =SysGetTick(); + StartAsyncJob(); + return 0; + } + else + return SetError(errCliJobPending); +} +//--------------------------------------------------------------------------- +int TSnap7Client::AsReadSZLList(PS7SZLList pUsrData, int &ItemsCount) +{ + if (!Job.Pending) + { + Job.Pending =true; + Job.Op =s7opReadSzlList; + Job.pData =pUsrData; + Job.pAmount =&ItemsCount; + Job.Amount =ItemsCount; + JobStart =SysGetTick(); + StartAsyncJob(); + return 0; + } + else + return SetError(errCliJobPending); +} +//--------------------------------------------------------------------------- +int TSnap7Client::AsUpload(int BlockType, int BlockNum, void * pUsrData, int & Size) +{ + if (!Job.Pending) + { + Job.Pending =true; + Job.Op =s7opUpload; + Job.Area =BlockType; + Job.pData =pUsrData; + Job.pAmount =&Size; + Job.Amount =Size; + Job.Number =BlockNum; + Job.IParam =0; // not full upload, only data + JobStart =SysGetTick(); + StartAsyncJob(); + return 0; + } + else + return SetError(errCliJobPending); +} +//--------------------------------------------------------------------------- +int TSnap7Client::AsFullUpload(int BlockType, int BlockNum, void * pUsrData, int & Size) +{ + if (!Job.Pending) + { + Job.Pending =true; + Job.Op =s7opUpload; + Job.Area =BlockType; + Job.pData =pUsrData; + Job.pAmount =&Size; + Job.Amount =Size; + Job.Number =BlockNum; + Job.IParam =1; // full upload + JobStart =SysGetTick(); + StartAsyncJob(); + return 0; + } + else + return SetError(errCliJobPending); +} +//--------------------------------------------------------------------------- +int TSnap7Client::AsDownload(int BlockNum, void * pUsrData, int Size) +{ + if (!Job.Pending) + { + // Checks the size : here we only need a size>0 to avoid problems during + // doublebuffering, the real test of the block size will be done in + // Checkblock. + if (Size<1) + return SetError(errCliInvalidBlockSize); + Job.Pending =true; + Job.Op =s7opDownload; + // Doublebuffering + memcpy(&opData, pUsrData, Size); + Job.Number =BlockNum; + Job.Amount =Size; + JobStart =SysGetTick(); + StartAsyncJob(); + return 0; + } + else + return SetError(errCliJobPending); +} +//--------------------------------------------------------------------------- +int TSnap7Client::AsCopyRamToRom(int Timeout) +{ + if (!Job.Pending) + { + Job.Pending =true; + Job.Op =s7opCopyRamToRom; + if (Timeout>0) + { + Job.IParam =Timeout; + JobStart =SysGetTick(); + StartAsyncJob(); + return 0; + } + else + return SetError(errCliInvalidParams); + } + else + return SetError(errCliJobPending); +} +//--------------------------------------------------------------------------- +int TSnap7Client::AsCompress(int Timeout) +{ + if (!Job.Pending) + { + Job.Pending =true; + Job.Op =s7opCompress; + if (Timeout>0) + { + Job.IParam =Timeout; + JobStart =SysGetTick(); + StartAsyncJob(); + return 0; + } + else + return SetError(errCliInvalidParams); + } + else + return SetError(errCliJobPending); +} +//--------------------------------------------------------------------------- +int TSnap7Client::AsDBRead(int DBNumber, int Start, int Size, void * pUsrData) +{ + return AsReadArea(S7AreaDB, DBNumber, Start, Size, S7WLByte, pUsrData); +} +//--------------------------------------------------------------------------- +int TSnap7Client::AsDBWrite(int DBNumber, int Start, int Size, void * pUsrData) +{ + return AsWriteArea(S7AreaDB, DBNumber, Start, Size, S7WLByte, pUsrData); +} +//--------------------------------------------------------------------------- +int TSnap7Client::AsMBRead(int Start, int Size, void * pUsrData) +{ + return AsReadArea(S7AreaMK, 0, Start, Size, S7WLByte, pUsrData); +} +//--------------------------------------------------------------------------- +int TSnap7Client::AsMBWrite(int Start, int Size, void * pUsrData) +{ + return AsWriteArea(S7AreaMK, 0, Start, Size, S7WLByte, pUsrData); +} +//--------------------------------------------------------------------------- +int TSnap7Client::AsEBRead(int Start, int Size, void * pUsrData) +{ + return AsReadArea(S7AreaPE, 0, Start, Size, S7WLByte, pUsrData); +} +//--------------------------------------------------------------------------- +int TSnap7Client::AsEBWrite(int Start, int Size, void * pUsrData) +{ + return AsWriteArea(S7AreaPE, 0, Start, Size, S7WLByte, pUsrData); +} +//--------------------------------------------------------------------------- +int TSnap7Client::AsABRead(int Start, int Size, void * pUsrData) +{ + return AsReadArea(S7AreaPA, 0, Start, Size, S7WLByte, pUsrData); +} +//--------------------------------------------------------------------------- +int TSnap7Client::AsABWrite(int Start, int Size, void * pUsrData) +{ + return AsWriteArea(S7AreaPA, 0, Start, Size, S7WLByte, pUsrData); +} +//--------------------------------------------------------------------------- +int TSnap7Client::AsTMRead(int Start, int Amount, void * pUsrData) +{ + return AsReadArea(S7AreaTM, 0, Start, Amount, S7WLTimer, pUsrData); +} +//--------------------------------------------------------------------------- +int TSnap7Client::AsTMWrite(int Start, int Amount, void * pUsrData) +{ + return AsWriteArea(S7AreaTM, 0, Start, Amount, S7WLTimer, pUsrData); +} +//--------------------------------------------------------------------------- +int TSnap7Client::AsCTRead(int Start, int Amount, void * pUsrData) +{ + return AsReadArea(S7AreaCT, 0, Start, Amount, S7WLCounter, pUsrData); +} +//--------------------------------------------------------------------------- +int TSnap7Client::AsCTWrite(int Start, int Amount, void * pUsrData) +{ + return AsWriteArea(S7AreaCT, 0, Start, Amount, S7WLCounter, pUsrData); +} +//--------------------------------------------------------------------------- +int TSnap7Client::AsDBGet(int DBNumber, void * pUsrData, int &Size) +{ + if (!Job.Pending) + { + if (Size<=0) + return SetError(errCliInvalidBlockSize); + Job.Pending =true; + Job.Op =s7opDBGet; + Job.Number =DBNumber; + Job.pData =pUsrData; + Job.pAmount =&Size; + Job.Amount =Size; + JobStart =SysGetTick(); + StartAsyncJob(); + return 0; + } + else + return SetError(errCliJobPending); +} +//--------------------------------------------------------------------------- +int TSnap7Client::AsDBFill(int DBNumber, int FillChar) +{ + if (!Job.Pending) + { + Job.Pending =true; + Job.Op =s7opDBFill; + Job.Number =DBNumber; + Job.IParam =FillChar; + JobStart =SysGetTick(); + StartAsyncJob(); + return 0; + } + else + return SetError(errCliJobPending); +} +//--------------------------------------------------------------------------- +void TSnap7Client::StartAsyncJob() +{ + ClrError(); + if (!ThreadCreated) + { + EvtJob = new TSnapEvent(false); + EvtComplete = new TSnapEvent(false); + OpenThread(); + ThreadCreated=true; + } + EvtComplete->Reset(); // reset if previously was not called WaitAsCompletion + EvtJob->Set(); +} +//--------------------------------------------------------------------------- +int TSnap7Client::WaitAsCompletion(unsigned long Timeout) +{ + if (Job.Pending) + { + if (ThreadCreated) + { + if (EvtComplete->WaitFor(Timeout)==WAIT_OBJECT_0) + return Job.Result; + else + { + if (Destroying) + return errCliDestroying; + else + return SetError(errCliJobTimeout); + } + } + else + return SetError(errCliJobTimeout); + } + else + return Job.Result; +} +//--------------------------------------------------------------------------- +void TClientThread::Execute() +{ + while (!Terminated) + { + FClient->EvtJob->WaitForever(); + if (!Terminated) + { + FClient->PerformOperation(); + FClient->EvtComplete->Set(); + // Notify the caller the end of job (if callback is set) + FClient->DoCompletion(); + } + }; +} + diff --git a/utils/snap7_src/src/core/s7_client.h b/utils/snap7_src/src/core/s7_client.h new file mode 100644 index 0000000..2cd4fe7 --- /dev/null +++ b/utils/snap7_src/src/core/s7_client.h @@ -0,0 +1,104 @@ +/*=============================================================================| +| 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/ | +|=============================================================================*/ +#ifndef s7_client_h +#define s7_client_h +//--------------------------------------------------------------------------- +#include "snap_threads.h" +#include "s7_micro_client.h" +//--------------------------------------------------------------------------- + +extern "C" { +typedef void (S7API *pfn_CliCompletion) (void * usrPtr, int opCode, int opResult); +} +class TSnap7Client; + +class TClientThread: public TSnapThread +{ +private: + TSnap7Client * FClient; +public: + TClientThread(TSnap7Client *Client) + { + FClient = Client; + } + void Execute(); +}; +//--------------------------------------------------------------------------- +class TSnap7Client: public TSnap7MicroClient +{ +private: + TClientThread *FThread; + bool ThreadCreated; + void CloseThread(); + void OpenThread(); + void StartAsyncJob(); +protected: + PSnapEvent EvtJob; + PSnapEvent EvtComplete; + pfn_CliCompletion CliCompletion; + void *FUsrPtr; + void DoCompletion(); +public: + friend class TClientThread; + TSnap7Client(); + ~TSnap7Client(); + int Reset(bool DoReconnect); + int SetAsCallback(pfn_CliCompletion pCompletion, void * usrPtr); + int GetParam(int ParamNumber, void *pValue); + int SetParam(int ParamNumber, void *pValue); + // Async functions + bool CheckAsCompletion( int & opResult); + int WaitAsCompletion(unsigned long Timeout); + int AsReadArea(int Area, int DBNumber, int Start, int Amount, int WordLen, void * pUsrData); + int AsWriteArea(int Area, int DBNumber, int Start, int Amount, int WordLen, void * pUsrData); + int AsListBlocksOfType(int BlockType, PS7BlocksOfType pUsrData, int & ItemsCount); + int AsReadSZL(int ID, int Index, PS7SZL pUsrData, int & Size); + int AsReadSZLList(PS7SZLList pUsrData, int &ItemsCount); + int AsUpload(int BlockType, int BlockNum, void * pUsrData, int & Size); + int AsFullUpload(int BlockType, int BlockNum, void * pUsrData, int & Size); + int AsDownload(int BlockNum, void * pUsrData, int Size); + int AsCopyRamToRom(int Timeout); + int AsCompress(int Timeout); + int AsDBRead(int DBNumber, int Start, int Size, void * pUsrData); + int AsDBWrite(int DBNumber, int Start, int Size, void * pUsrData); + int AsMBRead(int Start, int Size, void * pUsrData); + int AsMBWrite(int Start, int Size, void * pUsrData); + int AsEBRead(int Start, int Size, void * pUsrData); + int AsEBWrite(int Start, int Size, void * pUsrData); + int AsABRead(int Start, int Size, void * pUsrData); + int AsABWrite(int Start, int Size, void * pUsrData); + int AsTMRead(int Start, int Amount, void * pUsrData); + int AsTMWrite(int Start, int Amount, void * pUsrData); + int AsCTRead(int Start, int Amount, void * pUsrData); + int AsCTWrite(int Start, int Amount, void * pUsrData); + int AsDBGet(int DBNumber, void * pUsrData, int & Size); + int AsDBFill(int DBNumber, int FillChar); +}; + +typedef TSnap7Client *PSnap7Client; + +//--------------------------------------------------------------------------- +#endif // s7_client_h diff --git a/utils/snap7_src/src/core/s7_firmware.h b/utils/snap7_src/src/core/s7_firmware.h new file mode 100644 index 0000000..e1c431e --- /dev/null +++ b/utils/snap7_src/src/core/s7_firmware.h @@ -0,0 +1,1256 @@ +/*=============================================================================| +| 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/ | +|=============================================================================*/ +#ifndef s7_firmware_h +#define s7_firmware_h +//--------------------------------------------------------------------------- + +#include "snap_platform.h" + +//****************************************************************************** +// CPU DATABANK +//****************************************************************************** + + byte SZLNotAvail[4] = { + 0x0A,0x00,0x00,0x00 + }; + + byte SZLSysState[6] = { + 0xFF,0x09,0x00,0x02,0x02,0x00 + }; + + byte SZL_ID_0000_IDX_XXXX[236] = { + 0xFF,0x09,0x00,0xE8,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x70,0x00,0x00,0x0F,0x00,0x00,0x02,0x00, + 0x11,0x01,0x11,0x0F,0x11,0x00,0x12,0x01,0x12,0x0F,0x12,0x00,0x13,0x01,0x13,0x00,0x14,0x0F,0x14, + 0x00,0x15,0x01,0x15,0x00,0x17,0x01,0x17,0x0F,0x17,0x00,0x18,0x01,0x18,0x0F,0x18,0x00,0x19,0x0F, + 0x19,0x00,0x1A,0x0F,0x1A,0x00,0x1B,0x0F,0x1B,0x00,0x1C,0x01,0x1C,0x0F,0x1C,0x00,0x21,0x0A,0x21, + 0x0F,0x21,0x02,0x22,0x00,0x23,0x0F,0x23,0x00,0x24,0x01,0x24,0x04,0x24,0x05,0x24,0x00,0x25,0x01, + 0x25,0x02,0x25,0x0F,0x25,0x01,0x31,0x01,0x32,0x02,0x32,0x00,0x36,0x01,0x36,0x0F,0x36,0x00,0x37, + 0x01,0x37,0x0F,0x37,0x00,0x38,0x01,0x38,0x02,0x38,0x0F,0x38,0x01,0x39,0x00,0x3A,0x0F,0x3A,0x00, + 0x74,0x01,0x74,0x0F,0x74,0x05,0x91,0x0A,0x91,0x0C,0x91,0x0D,0x91,0x00,0x92,0x02,0x92,0x06,0x92, + 0x0F,0x92,0x00,0x94,0x01,0x94,0x02,0x94,0x06,0x94,0x07,0x94,0x0F,0x94,0x00,0x95,0x01,0x95,0x0F, + 0x95,0x06,0x96,0x0C,0x96,0x0C,0x97,0x0D,0x97,0x01,0x9A,0x02,0x9A,0x0F,0x9A,0x0C,0x9B,0x00,0x9C, + 0x01,0x9C,0x02,0x9C,0x03,0x9C,0x0F,0x9C,0x00,0xA0,0x01,0xA0,0x0F,0xA0,0x00,0xB1,0x00,0xB2,0x00, + 0xB3,0x00,0xB4,0x01,0xB5,0x02,0xB5,0x03,0xB5,0x04,0xB5,0x05,0xB5,0x06,0xB5,0x07,0xB5,0x08,0xB5, + 0x01,0xB6,0x02,0xB6,0x03,0xB6,0x04,0xB6 + }; + + byte SZL_ID_0F00_IDX_XXXX[12] = { + 0xFF,0x09,0x00,0x08,0x0F,0x00,0x00,0x00,0x00,0x02,0x00,0x70 + }; + + byte SZL_ID_0002_IDX_XXXX[458] = { // <--Wrapped to 458 bytes + 0xFF,0x09,0x01,0xC6,0x00,0x02,0x00,0x00,0x00,0x08,0x00,0x71,0x01,0xAC,0x00,0x01,0x00,0x28,0x00, + 0x1C,0x01,0xAC,0x24,0x00,0x00,0x24,0x00,0x00,0x01,0xAC,0x23,0x00,0x00,0x06,0x04,0xB0,0x01,0xAC, + 0x22,0x00,0x00,0x08,0x00,0x01,0x01,0xAC,0x31,0x00,0x04,0x00,0x00,0x01,0x01,0xAC,0x12,0xFF,0x00, + 0x08,0x00,0x01,0x01,0xAC,0x12,0x31,0x00,0x08,0x00,0x01,0x01,0xAD,0x00,0x00,0x80,0x00,0x00,0x01, + 0x01,0xAD,0x01,0x00,0x80,0x00,0x00,0x01,0x01,0xAD,0x02,0x00,0x80,0x00,0x00,0x01,0x01,0xAD,0x03, + 0x00,0x80,0x00,0x00,0x01,0x01,0xAD,0x04,0x00,0x80,0x00,0x00,0x01,0x01,0xAD,0x05,0x00,0x80,0x00, + 0x00,0x01,0x01,0xAD,0x06,0x00,0x80,0x00,0x00,0x01,0x01,0xAD,0x07,0x00,0x80,0x00,0x00,0x01,0x01, + 0xAD,0x00,0x01,0x80,0x00,0x00,0x01,0x01,0xAD,0x01,0x01,0x80,0x00,0x00,0x01,0x01,0xAD,0x02,0x01, + 0x80,0x00,0x00,0x01,0x01,0xAD,0x03,0x01,0x80,0x00,0x00,0x01,0x01,0xAD,0x04,0x01,0x80,0x00,0x00, + 0x01,0x01,0xAD,0x05,0x01,0x80,0x00,0x00,0x01,0x01,0xAD,0x06,0x01,0x80,0x00,0x00,0x01,0x01,0xAD, + 0x07,0x01,0x80,0x00,0x00,0x01,0x01,0xAD,0x00,0x03,0x80,0x00,0x00,0x01,0x01,0xAD,0x01,0x03,0x80, + 0x00,0x00,0x01,0x01,0xAD,0x02,0x03,0x80,0x00,0x00,0x01,0x01,0xAD,0x03,0x03,0x80,0x00,0x00,0x01, + 0x01,0xAD,0x04,0x03,0x80,0x00,0x00,0x01,0x01,0xAD,0x05,0x03,0x80,0x00,0x00,0x01,0x01,0xAD,0x06, + 0x03,0x80,0x00,0x00,0x01,0x01,0xAD,0x07,0x03,0x80,0x00,0x00,0x01,0x01,0xAD,0x00,0x04,0x80,0x00, + 0x00,0x01,0x01,0xAD,0x01,0x04,0x80,0x00,0x00,0x01,0x01,0xAD,0x02,0x04,0x80,0x00,0x00,0x01,0x01, + 0xAD,0x03,0x04,0x80,0x00,0x00,0x01,0x01,0xAD,0x04,0x04,0x80,0x00,0x00,0x01,0x01,0xAD,0x05,0x04, + 0x80,0x00,0x00,0x01,0x01,0xAD,0x06,0x04,0x80,0x00,0x00,0x01,0x01,0xAD,0x07,0x04,0x80,0x00,0x00, + 0x01,0x01,0xAD,0x00,0x05,0x80,0x00,0x00,0x01,0x01,0xAD,0x01,0x05,0x80,0x00,0x00,0x01,0x01,0xAD, + 0x02,0x05,0x80,0x00,0x00,0x01,0x01,0xAD,0x03,0x05,0x80,0x00,0x00,0x01,0x01,0xAD,0x04,0x05,0x80, + 0x00,0x00,0x01,0x01,0xAD,0x05,0x05,0x80,0x00,0x00,0x01,0x01,0xAD,0x06,0x05,0x80,0x00,0x00,0x01, + 0x01,0xAD,0x07,0x05,0x80,0x00,0x00,0x01,0x01,0xAD,0x00,0x06,0x80,0x00,0x00,0x01,0x01,0xAD,0x01, + 0x06,0x80,0x00,0x00,0x01,0x01,0xAD,0x02,0x06,0x80,0x00,0x00,0x01,0x01,0xAD,0x03,0x06,0x80,0x00, + 0x00,0x01,0x01,0xAD,0x04,0x06,0x80,0x00,0x00,0x01,0x01,0xAD,0x05,0x06,0x80,0x00,0x00,0x01,0x01, + 0xAD,0x06,0x06,0x80,0x00,0x00,0x01,0x01,0xAD,0x07,0x06,0x80,0x00,0x00,0x01,0x01,0xAD,0x00,0x07, + 0x80,0x00 + }; + + byte SZL_ID_0011_IDX_XXXX[124] = { + 0xFF,0x09,0x00,0x78,0x00,0x11,0x00,0x00,0x00,0x1C,0x00,0x04,0x00,0x01,0x36,0x45,0x53,0x37,0x20, + 0x33,0x31,0x35,0x2D,0x32,0x45,0x48,0x31,0x34,0x2D,0x30,0x41,0x42,0x30,0x20,0x00,0xC0,0x00,0x04, + 0x00,0x01,0x00,0x06,0x36,0x45,0x53,0x37,0x20,0x33,0x31,0x35,0x2D,0x32,0x45,0x48,0x31,0x34,0x2D, + 0x30,0x41,0x42,0x30,0x20,0x00,0xC0,0x00,0x04,0x00,0x01,0x00,0x07,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0xC0,0x56,0x03,0x02, + 0x06,0x00,0x81,0x42,0x6F,0x6F,0x74,0x20,0x4C,0x6F,0x61,0x64,0x65,0x72,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x00,0x00,0x41,0x20,0x09,0x09 + }; + + byte SZL_ID_0012_IDX_XXXX[58] = { + 0xFF,0x09,0x00,0x36,0x00,0x12,0x00,0x00,0x00,0x02,0x00,0x17,0x00,0x01,0x01,0x01,0x01,0x04,0x03, + 0x02,0x03,0x03,0x03,0x04,0x03,0x06,0x03,0x07,0x03,0x08,0x03,0x09,0x03,0x0A,0x03,0x0B,0x03,0x0C, + 0x03,0x0D,0x03,0x0E,0x03,0x0F,0x03,0x10,0x03,0x11,0x03,0x12,0x03,0x13,0x03,0x14,0x03,0x15,0x03, + 0x17 + }; + + byte SZL_ID_0013_IDX_XXXX[192] = { + 0xFF,0x09,0x00,0xBC,0x00,0x13,0x00,0x00,0x00,0x24,0x00,0x05,0x00,0x01,0x00,0x01,0x00,0x06,0x00, + 0x00,0x00,0x11,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x26,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x02,0x00,0x08,0x00,0x00,0x00,0x02,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00, + 0x0C,0x06,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x02,0x00,0x80,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x02,0x00,0x00,0x0C,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x00,0x20,0x00,0x00, + 0x00,0x00 + }; + + byte SZL_ID_0014_IDX_XXXX[84] = { + 0xFF,0x09,0x00,0x50,0x00,0x14,0x00,0x00,0x00,0x08,0x00,0x09,0x00,0x01,0x00,0x01,0x08,0x00,0x00, + 0x00,0x00,0x02,0x00,0x01,0x08,0x00,0x00,0x00,0x00,0x03,0x00,0x01,0x40,0x00,0x00,0x80,0x00,0x04, + 0x00,0x01,0x01,0x00,0x00,0x00,0x00,0x05,0x00,0x01,0x01,0x00,0x00,0x08,0x00,0x06,0x00,0x01,0x08, + 0x00,0x00,0x00,0x00,0x07,0x00,0x01,0x80,0x00,0x00,0x00,0x00,0x08,0x00,0x01,0x08,0x00,0x00,0x10, + 0x00,0x09,0x00,0x01,0x00,0x20,0x00,0x00 + }; + + byte SZL_ID_0015_IDX_XXXX[62] = { + 0xFF,0x09,0x00,0x3A,0x00,0x15,0x00,0x00,0x00,0x0A,0x00,0x05,0x08,0x00,0x00,0x16,0x03,0xD1,0x00, + 0x00,0xFF,0xFE,0x0A,0x00,0x3E,0x81,0x03,0xD1,0x00,0x00,0xFF,0xFE,0x0B,0x00,0x04,0x22,0x03,0xD1, + 0x00,0x00,0xFF,0xFE,0x0C,0x00,0x1F,0x40,0x03,0xD1,0x00,0x00,0xFF,0xFE,0x0E,0x00,0x1F,0x40,0x03, + 0xD1,0x00,0x00,0xFF,0xFE + }; + + byte SZL_ID_0F14_IDX_XXXX[12] = { + 0xFF,0x09,0x00,0x08,0x0F,0x14,0x00,0x00,0x00,0x08,0x00,0x09 + }; + + byte SZL_ID_0019_IDX_XXXX[40] = { + 0xFF,0x09,0x00,0x24,0x00,0x19,0x00,0x00,0x00,0x04,0x00,0x07,0x00,0x01,0x00,0x00,0x00,0x04,0x01, + 0x00,0x00,0x05,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x15, + 0x00,0x00 + }; + + byte SZL_ID_0F19_IDX_XXXX[12] = { + 0xFF,0x09,0x00,0x08,0x0F,0x19,0x00,0x00,0x00,0x04,0x00,0x07 + }; + + byte SZL_ID_001C_IDX_XXXX[352] = { + 0xFF,0x09,0x01,0x5C,0x00,0x1C,0x00,0x00,0x00,0x22,0x00,0x0A,0x00,0x01,0x53,0x4E,0x41,0x50,0x37, + 0x2D,0x53,0x45,0x52,0x56,0x45,0x52,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x43,0x50,0x55,0x20,0x33,0x31,0x35,0x2D,0x32, + 0x20,0x50,0x4E,0x2F,0x44,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x04,0x4F,0x72,0x69,0x67,0x69,0x6E,0x61,0x6C,0x20,0x53,0x69,0x65,0x6D,0x65,0x6E,0x73,0x20, + 0x45,0x71,0x75,0x69,0x70,0x6D,0x65,0x6E,0x74,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x53,0x20, + 0x43,0x2D,0x43,0x32,0x55,0x52,0x32,0x38,0x39,0x32,0x32,0x30,0x31,0x32,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x43,0x50,0x55,0x20,0x33,0x31, + 0x35,0x2D,0x32,0x20,0x50,0x4E,0x2F,0x44,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x4D,0x4D,0x43,0x20,0x32,0x36,0x37,0x46,0x46,0x31, + 0x31,0x46,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x09,0x00,0x2A,0xF6,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x0A,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + }; + + byte SZL_ID_0F1C_IDX_XXXX[12] = { + 0xFF,0x09,0x00,0x08,0x0F,0x1C,0x00,0x00,0x00,0x22,0x00,0x0A + }; + + byte SZL_ID_0036_IDX_XXXX[12] = { + 0xFF,0x09,0x00,0x08,0x00,0x36,0x00,0x00,0x00,0x00,0x00,0x00 + }; + + byte SZL_ID_0F36_IDX_XXXX[12] = { + 0xFF,0x09,0x00,0x08,0x0F,0x36,0x00,0x00,0x00,0x00,0x00,0x00 + }; + + byte SZL_ID_0025_IDX_XXXX[16] = { + 0xFF,0x09,0x00,0x0C,0x00,0x25,0x00,0x00,0x00,0x04,0x00,0x01,0x01,0x0C,0x3D,0x00 + }; + + byte SZL_ID_0F25_IDX_XXXX[12] = { + 0xFF,0x09,0x00,0x08,0x0F,0x25,0x00,0x00,0x00,0x04,0x00,0x01 + }; + + byte SZL_ID_0037_IDX_XXXX[60] = { + 0xFF,0x09,0x00,0x38,0x00,0x37,0x00,0x00,0x00,0x30,0x00,0x01,0x07,0xFE,0xC0,0xA8,0x01,0x0A,0xFF, + 0xFF,0xFF,0x00,0xC0,0xA8,0x01,0x0A,0x00,0x1B,0x1B,0x1D,0x1A,0x2D,0x01,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x8F,0x88,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00 + }; + + byte SZL_ID_0F37_IDX_XXXX[12] = { + 0xFF,0x09,0x00,0x08,0x0F,0x37,0x00,0x00,0x00,0x30,0x00,0x01 + }; + + byte SZL_ID_0074_IDX_XXXX[40] = { + 0xFF,0x09,0x00,0x24,0x00,0x74,0x00,0x00,0x00,0x04,0x00,0x07,0x00,0x01,0x00,0x00,0x00,0x04,0x01, + 0x00,0x00,0x05,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x15, + 0x00,0x00 + }; + + byte SZL_ID_0F74_IDX_XXXX[12] = { + 0xFF,0x09,0x00,0x08,0x0F,0x74,0x00,0x00,0x00,0x04,0x00,0x07 + }; + + byte SZL_ID_0591_IDX_XXXX[76] = { + 0xFF,0x09,0x00,0x48,0x05,0x91,0x00,0x00,0x00,0x10,0x00,0x04,0x00,0x00,0x02,0x01,0x07,0xFF,0xC4, + 0xC0,0xC4,0xC0,0x00,0x00,0xB4,0x02,0x00,0x11,0x00,0x00,0x02,0x02,0x07,0xFE,0xA7,0xC4,0xA7,0xC4, + 0x00,0x00,0xB4,0x02,0x00,0x11,0x00,0x00,0x02,0x03,0x07,0xFD,0x97,0xC5,0x97,0xC5,0x00,0x00,0xB4, + 0x02,0x00,0x11,0x00,0x00,0x02,0x04,0x07,0xFC,0x97,0xC5,0x97,0xC5,0x00,0x00,0xB4,0x02,0x00,0x11 + }; + + byte SZL_ID_0A91_IDX_XXXX[44] = { + 0xFF,0x09,0x00,0x28,0x0A,0x91,0x00,0x00,0x00,0x10,0x00,0x02,0x01,0x00,0x02,0x01,0x07,0xFF,0xC4, + 0xC0,0xC4,0xC0,0x00,0x01,0xFE,0x02,0x00,0x11,0x80,0x00,0x00,0x00,0x07,0xFB,0xA7,0xC4,0xA7,0xC4, + 0x00,0x01,0xFE,0x02,0x00,0x11 + }; + + byte SZL_ID_0F92_IDX_XXXX[12] = { + 0xFF,0x09,0x00,0x08,0x0F,0x92,0x00,0x00,0x00,0x10,0x00,0x01 + }; + + byte SZL_ID_0294_IDX_XXXX[270] = { + 0xFF,0x09,0x01,0x0A,0x02,0x94,0x00,0x00,0x01,0x02,0x00,0x01,0x00,0x00,0x03,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00 + }; + + byte SZL_ID_0794_IDX_XXXX[270] = { + 0xFF,0x09,0x01,0x0A,0x07,0x94,0x00,0x00,0x01,0x02,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00 + }; + + byte SZL_ID_0F94_IDX_XXXX[12] = { + 0xFF,0x09,0x00,0x08,0x0F,0x94,0x00,0x00,0x01,0x02,0x00,0x01 + }; + + byte SZL_ID_0095_IDX_XXXX[52] = { + 0xFF,0x09,0x00,0x30,0x00,0x95,0x00,0x00,0x00,0x28,0x00,0x01,0x64,0x00,0x02,0x02,0x07,0xFE,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + }; + + byte SZL_ID_0F95_IDX_XXXX[12] = { + 0xFF,0x09,0x00,0x08,0x0F,0x95,0x00,0x00,0x00,0x28,0x00,0x01 + }; + + byte SZL_ID_00A0_IDX_XXXX[212] = { + 0xFF,0x09,0x00,0xD0,0x00,0xA0,0x00,0x00,0x00,0x14,0x00,0x0A,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00, + 0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x38,0x86,0x13,0x81,0xFE,0x64,0xC7,0x72, + 0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x38,0x76,0x43,0x01,0xFF,0x46,0xC7, + 0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x29,0x66,0x43,0x04,0xFF,0x84, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04,0x23,0x52,0x13,0x90,0x16,0x43,0x02,0xFF, + 0x68,0xC7,0x00,0x00,0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x88,0x56,0x13,0x81, + 0xFE,0x64,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x88,0x46,0x43, + 0x01,0xFF,0x46,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x79,0x46, + 0x43,0x04,0xFF,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04,0x23,0x51,0x19,0x48, + 0x26,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x50,0x45, + 0x09,0x96,0x13,0x81,0xFE,0x64,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x50, + 0x45,0x09,0x86 + }; + + byte SZL_ID_0FA0_IDX_XXXX[12] = { + 0xFF,0x09,0x00,0x08,0x0F,0xA0,0x00,0x00,0x00,0x14,0x01,0xF4 + }; + + byte SZL_ID_0017_IDX_XXXX[458] = { // <--Wrapped to 458 bytes + 0xFF,0x09,0x01,0xC6,0x00,0x17,0x00,0x00,0x00,0x04,0x00,0x73,0x00,0x00,0x00,0x01,0x00,0x01,0x00, + 0x03,0x00,0x02,0x00,0x02,0x00,0x03,0x00,0x01,0x00,0x04,0x00,0x01,0x00,0x07,0x00,0x01,0x00,0x16, + 0x00,0x01,0x00,0x64,0x00,0x01,0x00,0x65,0x00,0x01,0x00,0x66,0x00,0x01,0x00,0x67,0x00,0x01,0x00, + 0x7A,0x00,0x01,0x00,0xC8,0x00,0x01,0x00,0xD2,0x00,0x01,0x02,0xBC,0x00,0x01,0x02,0xBD,0x00,0x01, + 0x02,0xBE,0x00,0x01,0x02,0xBF,0x00,0x01,0x02,0xC0,0x00,0x01,0x02,0xC1,0x00,0x01,0x02,0xC2,0x00, + 0x01,0x02,0xC3,0x00,0x01,0x02,0xC4,0x00,0x01,0x02,0xC5,0x00,0x01,0x02,0xC6,0x00,0x01,0x02,0xC7, + 0x00,0x01,0x02,0xC8,0x00,0x01,0x02,0xC9,0x00,0x01,0x02,0xCA,0x00,0x01,0x02,0xCB,0x00,0x01,0x02, + 0xCC,0x00,0x01,0x02,0xCD,0x00,0x01,0x02,0xCE,0x00,0x01,0x02,0xCF,0x00,0x01,0x02,0xD0,0x00,0x01, + 0x02,0xD1,0x00,0x01,0x02,0xD2,0x00,0x01,0x02,0xD3,0x00,0x01,0x02,0xD4,0x00,0x01,0x02,0xD5,0x00, + 0x01,0x02,0xD6,0x00,0x01,0x02,0xD7,0x00,0x01,0x02,0xD8,0x00,0x01,0x02,0xD9,0x00,0x01,0x02,0xDA, + 0x00,0x01,0x02,0xDB,0x00,0x01,0x02,0xDC,0x00,0x01,0x02,0xDD,0x00,0x01,0x02,0xDE,0x00,0x01,0x02, + 0xDF,0x00,0x01,0x02,0xE0,0x00,0x01,0x02,0xE1,0x00,0x01,0x02,0xE2,0x00,0x01,0x02,0xE3,0x00,0x01, + 0x02,0xE4,0x00,0x01,0x02,0xE5,0x00,0x01,0x02,0xE6,0x00,0x01,0x02,0xE7,0x00,0x01,0x02,0xE8,0x00, + 0x01,0x02,0xE9,0x00,0x01,0x02,0xEA,0x00,0x01,0x02,0xEB,0x00,0x01,0x02,0xEC,0x00,0x01,0x02,0xED, + 0x00,0x01,0x02,0xEE,0x00,0x01,0x02,0xEF,0x00,0x01,0x02,0xF0,0x00,0x01,0x02,0xF1,0x00,0x01,0x02, + 0xF2,0x00,0x01,0x02,0xF3,0x00,0x01,0x02,0xF4,0x00,0x01,0x02,0xF5,0x00,0x01,0x02,0xF6,0x00,0x01, + 0x02,0xF7,0x00,0x01,0x02,0xF8,0x00,0x01,0x02,0xF9,0x00,0x01,0x02,0xFA,0x00,0x01,0x02,0xFB,0x00, + 0x01,0x02,0xFC,0x00,0x01,0x02,0xFD,0x00,0x01,0x02,0xFE,0x00,0x01,0x02,0xFF,0x00,0x01,0x03,0x00, + 0x00,0x01,0x03,0x01,0x00,0x01,0x03,0x02,0x00,0x01,0x03,0x03,0x00,0x01,0x03,0x04,0x00,0x01,0x03, + 0x05,0x00,0x01,0x03,0x06,0x00,0x01,0x03,0x07,0x00,0x01,0x03,0x08,0x00,0x01,0x03,0x09,0x00,0x01, + 0x03,0x0A,0x00,0x01,0x03,0x0B,0x00,0x01,0x03,0x0C,0x00,0x01,0x03,0x0D,0x00,0x01,0x03,0x0E,0x00, + 0x01,0x03,0x0F,0x00,0x01,0x03,0x10,0x00,0x01,0x03,0x11,0x00,0x01,0x03,0x12,0x00,0x01,0x03,0x13, + 0x00,0x01,0x03,0x14,0x00,0x01,0x03,0x15,0x00,0x01,0x03,0x16,0x00,0x01,0x03,0x17,0x00,0x01,0x03, + 0x18,0x00,0x01,0x03,0x19,0x00,0x01,0x03,0x1A,0x00,0x01,0x03,0x1B,0x00,0x01,0x03,0x1C,0x00,0x01, + 0x03,0x1D + }; + + byte SZL_ID_0F17_IDX_XXXX[12] = { + 0xFF,0x09,0x00,0x08,0x0F,0x17,0x00,0x00,0x00,0x04,0x00,0x73 + }; + + byte SZL_ID_0018_IDX_XXXX[28] = { + 0xFF,0x09,0x00,0x18,0x00,0x18,0x00,0x00,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x08,0x00,0x01,0x00, + 0x08,0x00,0x02,0x00,0x08,0x00,0x03,0x00,0x08 + }; + + byte SZL_ID_0F18_IDX_XXXX[12] = { + 0xFF,0x09,0x00,0x08,0x0F,0x18,0x00,0x00,0x00,0x04,0x00,0x04 + }; + + byte SZL_ID_001A_IDX_XXXX[48] = { + 0xFF,0x09,0x00,0x2C,0x00,0x1A,0x00,0x00,0x00,0x0C,0x00,0x03,0x09,0x01,0x01,0x05,0x00,0x08,0x00, + 0x00,0x00,0x80,0x00,0x00,0x12,0x01,0x00,0x02,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x18,0x01, + 0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x02,0x00,0x00 + }; + + byte SZL_ID_0F1A_IDX_XXXX[12] = { + 0xFF,0x09,0x00,0x08,0x0F,0x1A,0x00,0x00,0x00,0x0C,0x00,0x03 + }; + + byte SZL_ID_001B_IDX_XXXX[132] = { + 0xFF,0x09,0x00,0x80,0x00,0x1B,0x00,0x00,0x00,0x14,0x00,0x06,0x09,0x00,0x00,0x00,0x00,0x02,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0C,0x06,0x00,0x00,0x00,0x00,0x09,0x00,0x00,0x70,0x00,0x02, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xF3,0xFA,0x00,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x26,0x00,0x00,0x00,0x00,0x12,0x00,0x00,0x70, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0xFF,0xDA,0x00,0x00,0x00,0x00,0x18,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x00, + 0x00,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00 + }; + + byte SZL_ID_0F1B_IDX_XXXX[12] = { + 0xFF,0x09,0x00,0x08,0x0F,0x1B,0x00,0x00,0x00,0x14,0x00,0x06 + }; + + byte SZL_ID_0021_IDX_XXXX[100] = { + 0xFF,0x09,0x00,0x60,0x00,0x21,0x00,0x00,0x00,0x04,0x00,0x16,0x01,0x01,0x01,0x01,0x01,0x11,0xFE, + 0x0A,0x01,0x21,0xFE,0x14,0x01,0x22,0xFE,0x15,0x01,0x33,0xFE,0x20,0x01,0x34,0xFE,0x21,0x01,0x35, + 0xFE,0x22,0x01,0x36,0xFE,0x23,0x01,0x41,0xFE,0x28,0x01,0x55,0xFE,0x37,0x01,0x56,0xFE,0x38,0x01, + 0x57,0xFE,0x39,0x01,0x64,0xFE,0x3D,0x00,0x01,0xFE,0x50,0x00,0x42,0xFE,0x52,0x00,0x61,0xFE,0x53, + 0x00,0xA1,0xFE,0x55,0x00,0xC1,0xFE,0x56,0x00,0xD2,0xFE,0x57,0x00,0x81,0xFE,0x64,0x00,0x21,0xFE, + 0x79,0x00,0x42,0xFE,0x7A + }; + + byte SZL_ID_0A21_IDX_XXXX[16] = { + 0xFF,0x09,0x00,0x0C,0x0A,0x21,0x00,0x00,0x00,0x04,0x00,0x01,0x01,0x01,0x01,0x01 + }; + + byte SZL_ID_0F21_IDX_XXXX[12] = { + 0xFF,0x09,0x00,0x08,0x0F,0x21,0x00,0x00,0x00,0x04,0x00,0x16 + }; + + byte SZL_ID_0023_IDX_XXXX[228] = { + 0xFF,0x09,0x00,0xE0,0x00,0x23,0x00,0x00,0x00,0x12,0x00,0x0C,0x1A,0x00,0x00,0x00,0x10,0xBE,0x00, + 0x00,0x08,0x00,0x0B,0xC0,0xFC,0x01,0xFF,0xFF,0xFF,0xF3,0x19,0x00,0x00,0x00,0x10,0x01,0x00,0x00, + 0x08,0x00,0x0B,0xC0,0xFC,0x01,0xFF,0xFF,0xFF,0xF3,0x10,0x00,0x00,0x00,0x10,0x26,0x00,0x00,0x08, + 0x00,0x0B,0xC0,0xFC,0x01,0xFF,0xFF,0xFF,0xF3,0x0C,0x00,0x00,0x00,0x10,0x01,0x00,0x00,0x08,0x00, + 0x0B,0xC0,0xFC,0x01,0xFF,0xFF,0xFF,0xF3,0x0B,0x00,0x00,0x00,0x10,0x01,0x00,0x00,0x08,0x00,0x0B, + 0xC0,0xFC,0x01,0xFF,0xFF,0xFF,0xF3,0x0A,0x00,0x00,0x00,0x10,0x01,0x00,0x00,0x08,0x00,0x0B,0xC0, + 0xFC,0x01,0xFF,0xFF,0xFF,0xF3,0x09,0x00,0x00,0x00,0x10,0x01,0x00,0x00,0x08,0x00,0x0B,0xC0,0xFC, + 0x01,0xFF,0xFF,0xFF,0xF3,0x04,0x00,0x00,0x00,0x10,0x01,0x00,0x00,0x08,0x00,0x0B,0xC0,0xFC,0x01, + 0xFF,0xFF,0xFF,0xF3,0x03,0x00,0x00,0x00,0x10,0x01,0x00,0x00,0x08,0x00,0x0B,0xC0,0xFC,0x01,0xFF, + 0xFF,0xFF,0xF3,0x02,0x00,0x00,0x00,0x10,0x16,0x00,0x00,0x08,0x00,0x0B,0xC0,0xFC,0x01,0xFF,0xFF, + 0xFF,0xF3,0x01,0x00,0x00,0x00,0x10,0x01,0x00,0x00,0x08,0x00,0x0B,0xC0,0xFC,0x01,0xFF,0xFF,0xFF, + 0xF3,0x1B,0x00,0x00,0x00,0x10,0x01,0x00,0x00,0x08,0x00,0x0B,0xC0,0xFC,0x01,0xFF,0xFF,0xFF,0xF3 + }; + + byte SZL_ID_0F23_IDX_XXXX[12] = { + 0xFF,0x09,0x00,0x08,0x0F,0x23,0x00,0x00,0x00,0x12,0x00,0x0C + }; + + byte SZL_ID_0024_IDX_XXXX[92] = { + 0xFF,0x09,0x00,0x58,0x00,0x24,0x00,0x00,0x00,0x14,0x00,0x04,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00, + 0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x38,0x86,0x43,0x01,0xFF,0x46,0xC7,0x72, + 0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x29,0x66,0x43,0x04,0xFF,0x84,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04,0x23,0x52,0x13,0x90,0x16,0x43,0x02,0xFF,0x68, + 0xC7,0x00,0x00,0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x88,0x56 + }; + + byte SZL_ID_0124_IDX_XXXX[32] = { + 0xFF,0x09,0x00,0x1C,0x01,0x24,0x00,0x00,0x00,0x14,0x00,0x01,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00, + 0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x38,0x86 + }; + + byte SZL_ID_0424_IDX_XXXX[32] = { + 0xFF,0x09,0x00,0x1C,0x04,0x24,0x00,0x00,0x00,0x14,0x00,0x01,0x51,0x44,0xFF, + 0x08, // <-- CPU Status + 0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x05,0x02,0x01,0x55,0x90,0x67 + }; + + byte SZL_ID_0038_IDX_XXXX[78] = { + 0xFF,0x09,0x00,0x4A,0x00,0x38,0x00,0x00,0x00,0x42,0x00,0x01,0x00,0x01,0x07,0xFE,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xAB,0xE3,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0xEE,0x49,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00 + }; + + byte SZL_ID_0F38_IDX_XXXX[12] = { + 0xFF,0x09,0x00,0x08,0x0F,0x38,0x00,0x00,0x00,0x42,0x00,0x01 + }; + + byte SZL_ID_003A_IDX_XXXX[12] = { + 0xFF,0x09,0x00,0x08,0x00,0x3A,0x00,0x00,0x00,0x94,0x00,0x00 + }; + + byte SZL_ID_0F3A_IDX_XXXX[12] = { + 0xFF,0x09,0x00,0x08,0x0F,0x3A,0x00,0x00,0x00,0x94,0x00,0x08 + }; + + byte SZL_ID_0F9A_IDX_XXXX[12] = { + 0xFF,0x09,0x00,0x08,0x0F,0x9A,0x00,0x00,0x01,0x1C,0x00,0x01 + }; + + byte SZL_ID_0D91_IDX_0000[28] = { + 0xFF,0x09,0x00,0x18,0x0D,0x91,0x00,0x00,0x00,0x10,0x00,0x01,0x00,0x00,0x02,0x00,0x7F,0xFF,0x00, + 0xC0,0x00,0xC0,0x00,0x00,0xB4,0x02,0x00,0x11 + }; + + byte SZL_ID_0092_IDX_0000[28] = { + 0xFF,0x09,0x00,0x18,0x00,0x92,0x00,0x00,0x00,0x10,0x00,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + }; + + byte SZL_ID_0292_IDX_0000[28] = { + 0xFF,0x09,0x00,0x18,0x02,0x92,0x00,0x00,0x00,0x10,0x00,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + }; + + byte SZL_ID_0692_IDX_0000[28] = { + 0xFF,0x09,0x00,0x18,0x06,0x92,0x00,0x00,0x00,0x10,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + }; + + byte SZL_ID_0094_IDX_0000[270] = { + 0xFF,0x09,0x01,0x0A,0x00,0x94,0x00,0x00,0x01,0x02,0x00,0x01,0x00,0x00,0x03,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00 + }; + + byte SZL_ID_0D97_IDX_0000[60] = { + 0xFF,0x09,0x00,0x38,0x0D,0x97,0x00,0x00,0x00,0x30,0x00,0x01,0x7F,0xFF,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x02,0x00,0x04,0x00,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00 + }; + + byte SZL_ID_0111_IDX_0001[40] = { + 0xFF,0x09,0x00,0x24,0x01,0x11,0x00,0x01,0x00,0x1C,0x00,0x01,0x00,0x01,0x36,0x45,0x53,0x37,0x20, + 0x33,0x31,0x35,0x2D,0x32,0x45,0x48,0x31,0x34,0x2D,0x30,0x41,0x42,0x30,0x20,0x00,0xC0,0x00,0x04, + 0x00,0x01 + }; + + byte SZL_ID_0111_IDX_0006[40] = { + 0xFF,0x09,0x00,0x24,0x01,0x11,0x00,0x06,0x00,0x1C,0x00,0x01,0x00,0x06,0x36,0x45,0x53,0x37,0x20, + 0x33,0x31,0x35,0x2D,0x32,0x45,0x48,0x31,0x34,0x2D,0x30,0x41,0x42,0x30,0x20,0x00,0xC0,0x00,0x04, + 0x00,0x01 + }; + + byte SZL_ID_0111_IDX_0007[40] = { + 0xFF,0x09,0x00,0x24,0x01,0x11,0x00,0x07,0x00,0x1C,0x00,0x01,0x00,0x07,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0xC0,0x56,0x03, + 0x02,0x06 + }; + + byte SZL_ID_0F11_IDX_0001[12] = { + 0xFF,0x09,0x00,0x08,0x0F,0x11,0x00,0x00,0x00,0x1C,0x00,0x04 + }; + + byte SZL_ID_0F11_IDX_0006[12] = { + 0xFF,0x09,0x00,0x08,0x0F,0x11,0x00,0x00,0x00,0x1C,0x00,0x04 + }; + + byte SZL_ID_0F11_IDX_0007[12] = { + 0xFF,0x09,0x00,0x08,0x0F,0x11,0x00,0x00,0x00,0x1C,0x00,0x04 + }; + + byte SZL_ID_0112_IDX_0000[14] = { + 0xFF,0x09,0x00,0x0A,0x01,0x12,0x00,0x00,0x00,0x02,0x00,0x01,0x00,0x01 + }; + + byte SZL_ID_0112_IDX_0100[16] = { + 0xFF,0x09,0x00,0x0C,0x01,0x12,0x01,0x00,0x00,0x02,0x00,0x02,0x01,0x01,0x01,0x04 + }; + + byte SZL_ID_0112_IDX_0200[12] = { + 0xFF,0x09,0x00,0x08,0x01,0x12,0x02,0x00,0x00,0x00,0x00,0x00 + }; + + byte SZL_ID_0112_IDX_0400[12] = { + 0xFF,0x09,0x00,0x08,0x01,0x12,0x04,0x00,0x00,0x00,0x00,0x00 + }; + + byte SZL_ID_0F12_IDX_0000[12] = { + 0xFF,0x09,0x00,0x08,0x0F,0x12,0x00,0x00,0x00,0x02,0x00,0x17 + }; + + byte SZL_ID_0F12_IDX_0100[12] = { + 0xFF,0x09,0x00,0x08,0x0F,0x12,0x00,0x00,0x00,0x02,0x00,0x17 + }; + + byte SZL_ID_0F12_IDX_0200[12] = { + 0xFF,0x09,0x00,0x08,0x0F,0x12,0x00,0x00,0x00,0x02,0x00,0x17 + }; + + byte SZL_ID_0F12_IDX_0400[12] = { + 0xFF,0x09,0x00,0x08,0x0F,0x12,0x00,0x00,0x00,0x02,0x00,0x17 + }; + + byte SZL_ID_0113_IDX_0001[48] = { + 0xFF,0x09,0x00,0x2C,0x01,0x13,0x00,0x01,0x00,0x24,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x06,0x00, + 0x00,0x00,0x11,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x26,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + }; + + byte SZL_ID_0115_IDX_0800[22] = { + 0xFF,0x09,0x00,0x12,0x01,0x15,0x08,0x00,0x00,0x0A,0x00,0x01,0x08,0x00,0x00,0x16,0x03,0xD1,0x00, + 0x00,0xFF,0xFE + }; + + byte SZL_ID_011C_IDX_0001[46] = { + 0xFF,0x09,0x00,0x2A,0x01,0x1C,0x00,0x01,0x00,0x22,0x00,0x01,0x00,0x01,0x53,0x4D,0x41,0x52,0x54, + 0x37,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + }; + + byte SZL_ID_011C_IDX_0002[46] = { + 0xFF,0x09,0x00,0x2A,0x01,0x1C,0x00,0x02,0x00,0x22,0x00,0x01,0x00,0x02,0x43,0x50,0x55,0x20,0x33, + 0x31,0x35,0x2D,0x32,0x20,0x50,0x4E,0x2F,0x44,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + }; + + byte SZL_ID_011C_IDX_0003[46] = { + 0xFF,0x09,0x00,0x2A,0x01,0x1C,0x00,0x03,0x00,0x22,0x00,0x01,0x00,0x03,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + }; + + byte SZL_ID_011C_IDX_0004[46] = { + 0xFF,0x09,0x00,0x2A,0x01,0x1C,0x00,0x04,0x00,0x22,0x00,0x01,0x00,0x04,0x4F,0x72,0x69,0x67,0x69, + 0x6E,0x61,0x6C,0x20,0x53,0x69,0x65,0x6D,0x65,0x6E,0x73,0x20,0x45,0x71,0x75,0x69,0x70,0x6D,0x65, + 0x6E,0x74,0x00,0x00,0x00,0x00,0x00,0x00 + }; + + byte SZL_ID_011C_IDX_0005[46] = { + 0xFF,0x09,0x00,0x2A,0x01,0x1C,0x00,0x05,0x00,0x22,0x00,0x01,0x00,0x05,0x53,0x20,0x43,0x2D,0x43, + 0x32,0x55,0x52,0x32,0x38,0x39,0x32,0x32,0x30,0x31,0x32,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + }; + + byte SZL_ID_011C_IDX_0007[46] = { + 0xFF,0x09,0x00,0x2A,0x01,0x1C,0x00,0x07,0x00,0x22,0x00,0x01,0x00,0x07,0x43,0x50,0x55,0x20,0x33, + 0x31,0x35,0x2D,0x32,0x20,0x50,0x4E,0x2F,0x44,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + }; + + byte SZL_ID_011C_IDX_0008[46] = { + 0xFF,0x09,0x00,0x2A,0x01,0x1C,0x00,0x08,0x00,0x22,0x00,0x01,0x00,0x08,0x4D,0x4D,0x43,0x20,0x32, + 0x36,0x37,0x46,0x46,0x31,0x31,0x46,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + }; + + byte SZL_ID_011C_IDX_0009[46] = { + 0xFF,0x09,0x00,0x2A,0x01,0x1C,0x00,0x09,0x00,0x22,0x00,0x01,0x00,0x09,0x00,0x2A,0xF6,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + }; + + byte SZL_ID_011C_IDX_000A[46] = { + 0xFF,0x09,0x00,0x2A,0x01,0x1C,0x00,0x0A,0x00,0x22,0x00,0x01,0x00,0x0A,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + }; + + byte SZL_ID_011C_IDX_000B[46] = { + 0xFF,0x09,0x00,0x2A,0x01,0x1C,0x00,0x0B,0x00,0x22,0x00,0x01,0x00,0x0B,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + }; + + byte SZL_ID_0222_IDX_0001[40] = { + 0xFF,0x09,0x00,0x24,0x02,0x22,0x00,0x01,0x00,0x1C,0x00,0x01,0x11,0x03,0x01,0x01,0xC8,0x58,0x00, + 0x00,0x00,0x00,0x00,0x01,0x94,0x02,0x05,0x02,0x01,0x56,0x64,0x77,0x00,0x10,0x00,0x08,0x00,0x00, + 0x00,0x00 + }; + + byte SZL_ID_0222_IDX_000A[40] = { + 0xFF,0x09,0x00,0x24,0x02,0x22,0x00,0x0A,0x00,0x1C,0x00,0x01,0x10,0x11,0x02,0x0A,0x00,0x50,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00 + }; + + byte SZL_ID_0222_IDX_0014[40] = { + 0xFF,0x09,0x00,0x24,0x02,0x22,0x00,0x14,0x00,0x1C,0x00,0x01,0x10,0x21,0x03,0x14,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00 + }; + + byte SZL_ID_0222_IDX_0028[40] = { + 0xFF,0x09,0x00,0x24,0x02,0x22,0x00,0x28,0x00,0x1C,0x00,0x01,0x10,0x41,0x10,0x28,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00 + }; + + byte SZL_ID_0222_IDX_0050[40] = { + 0xFF,0x09,0x00,0x24,0x02,0x22,0x00,0x50,0x00,0x1C,0x00,0x01,0x35,0x01,0xFE,0x50,0xC8,0x58,0x00, + 0x00,0x00,0x00,0x00,0x96,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00, + 0x00,0x00 + }; + + byte SZL_ID_0222_IDX_0064[40] = { + 0xFF,0x09,0x00,0x24,0x02,0x22,0x00,0x64,0x00,0x1C,0x00,0x01,0x13,0x81,0xFE,0x64,0xC7,0x72,0x43, + 0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x38,0x76,0x00,0x00,0x00,0x08,0x00,0x00, + 0x00,0x00 + }; + + byte SZL_ID_0125_IDX_0000[16] = { + 0xFF,0x09,0x00,0x0C,0x01,0x25,0x00,0x00,0x00,0x04,0x00,0x01,0x00,0x03,0x01,0x00 + }; + + byte SZL_ID_0125_IDX_0001[16] = { + 0xFF,0x09,0x00,0x0C,0x01,0x25,0x00,0x01,0x00,0x04,0x00,0x01,0x01,0x0C,0x3D,0x00 + }; + + byte SZL_ID_0225_IDX_0001[16] = { + 0xFF,0x09,0x00,0x0C,0x02,0x25,0x00,0x01,0x00,0x04,0x00,0x01,0x00,0x03,0x01,0x00 + }; + + byte SZL_ID_0132_IDX_0001[52] = { + 0xFF,0x09,0x00,0x30,0x01,0x32,0x00,0x01,0x00,0x28,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03 + }; + + byte SZL_ID_0132_IDX_0002[52] = { + 0xFF,0x09,0x00,0x30,0x01,0x32,0x00,0x02,0x00,0x28,0x00,0x01,0x00,0x02,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x0E,0x00,0x00,0x00,0x00,0x06,0x01,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + }; + + byte SZL_ID_0132_IDX_0003[52] = { + 0xFF,0x09,0x00,0x30,0x01,0x32,0x00,0x03,0x00,0x28,0x00,0x01,0x00,0x03,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + }; + + byte SZL_ID_0132_IDX_0004[52] = { + 0xFF,0x09,0x00,0x30,0x01,0x32,0x00,0x04,0x00,0x28,0x00,0x01,0x00,0x04,0x00,0x01,0x00,0x00,0x00, + 0x01,0x00,0x02,0x00,0x00,0x00,0x00,0x56,0x56,0x10,0x01,0x33,0x7B,0x02,0x00,0x75,0xF4,0x02,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + }; + + byte SZL_ID_0132_IDX_0005[52] = { + 0xFF,0x09,0x00,0x30,0x01,0x32,0x00,0x05,0x00,0x28,0x00,0x01,0x00,0x05,0x00,0x00,0x00,0x01,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + }; + + byte SZL_ID_0132_IDX_0006[52] = { + 0xFF,0x09,0x00,0x30,0x01,0x32,0x00,0x06,0x00,0x28,0x00,0x01,0x00,0x06,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + }; + + byte SZL_ID_0132_IDX_0007[52] = { + 0xFF,0x09,0x00,0x30,0x01,0x32,0x00,0x07,0x00,0x28,0x00,0x01,0x00,0x07,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + }; + + byte SZL_ID_0132_IDX_0008[52] = { + 0xFF,0x09,0x00,0x30,0x01,0x32,0x00,0x08,0x00,0x28,0x00,0x01,0x00,0x08,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x05,0x02, + 0x01,0x56,0x94,0x57,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + }; + + byte SZL_ID_0132_IDX_0009[52] = { + 0xFF,0x09,0x00,0x30,0x01,0x32,0x00,0x09,0x00,0x28,0x00,0x01,0x00,0x09,0x00,0x02,0xDC,0x6C,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + }; + + byte SZL_ID_0132_IDX_000A[52] = { + 0xFF,0x09,0x00,0x30,0x01,0x32,0x00,0x0A,0x00,0x28,0x00,0x01,0x00,0x0A,0x00,0x02,0xDC,0x6C,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + }; + + byte SZL_ID_0132_IDX_000B[52] = { + 0xFF,0x09,0x00,0x30,0x01,0x32,0x00,0x0B,0x00,0x28,0x00,0x01,0x00,0x0B,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + }; + + byte SZL_ID_0132_IDX_000C[52] = { + 0xFF,0x09,0x00,0x30,0x01,0x32,0x00,0x0C,0x00,0x28,0x00,0x01,0x00,0x0C,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + }; + + byte SZL_ID_0232_IDX_0001[52] = { + 0xFF,0x09,0x00,0x30,0x02,0x32,0x00,0x01,0x00,0x28,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03 + }; + + byte SZL_ID_0232_IDX_0004[52] = { + 0xFF,0x09,0x00,0x30,0x02,0x32,0x00,0x04,0x00,0x28,0x00,0x01,0x00,0x04,0x00,0x01,0x00,0x00,0x00, + 0x01,0x00,0x02,0x00,0x00,0x00,0x00,0x56,0x56,0x10,0x01,0x33,0x7B,0x02,0x00,0x75,0xF4,0x02,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + }; + + byte SZL_ID_0137_IDX_07FE[60] = { + 0xFF,0x09,0x00,0x38,0x01,0x37,0x07,0xFE,0x00,0x30,0x00,0x01,0x07,0xFE,0xC0,0xA8,0x01,0x0A,0xFF, + 0xFF,0xFF,0x00,0xC0,0xA8,0x01,0x0A,0x00,0x1B,0x1B,0x1D,0x1A,0x2D,0x01,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x8F,0x88,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00 + }; + + byte SZL_ID_0174_IDX_0001[16] = { + 0xFF,0x09,0x00,0x0C,0x01,0x74,0x00,0x01,0x00,0x04,0x00,0x01,0x00,0x01,0x00,0x00 + }; + + byte SZL_ID_0174_IDX_0004[16] = { + 0xFF,0x09,0x00,0x0C,0x01,0x74,0x00,0x04,0x00,0x04,0x00,0x01,0x00,0x04,0x01,0x00 + }; + + byte SZL_ID_0174_IDX_0005[16] = { + 0xFF,0x09,0x00,0x0C,0x01,0x74,0x00,0x05,0x00,0x04,0x00,0x01,0x00,0x05,0x00,0x00 + }; + + byte SZL_ID_0174_IDX_0006[16] = { + 0xFF,0x09,0x00,0x0C,0x01,0x74,0x00,0x06,0x00,0x04,0x00,0x01,0x00,0x06,0x00,0x00 + }; + + byte SZL_ID_0174_IDX_000B[16] = { + 0xFF,0x09,0x00,0x0C,0x01,0x74,0x00,0x0B,0x00,0x04,0x00,0x01,0x00,0x0B,0x00,0x00 + }; + + byte SZL_ID_0174_IDX_000C[16] = { + 0xFF,0x09,0x00,0x0C,0x01,0x74,0x00,0x0C,0x00,0x04,0x00,0x01,0x00,0x0C,0x00,0x00 + }; + + byte SZL_ID_0194_IDX_0064[270] = { + 0xFF,0x09,0x01,0x0A,0x01,0x94,0x00,0x64,0x01,0x02,0x00,0x01,0x00,0x64,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00 + }; + + byte SZL_ID_0694_IDX_0064[270] = { + 0xFF,0x09,0x01,0x0A,0x06,0x94,0x00,0x64,0x01,0x02,0x00,0x01,0x00,0x64,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00 + }; + + byte SZL_ID_01A0_IDX_0000[12] = { + 0xFF,0x09,0x00,0x08,0x01,0xA0,0x00,0x00,0x00,0x14,0x00,0x00 + }; + + byte SZL_ID_01A0_IDX_0001[32] = { + 0xFF,0x09,0x00,0x1C,0x01,0xA0,0x00,0x00,0x00,0x14,0x00,0x01,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00, + 0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x38,0x86 + }; + + byte SZL_ID_01A0_IDX_0002[52] = { + 0xFF,0x09,0x00,0x30,0x01,0xA0,0x00,0x00,0x00,0x14,0x00,0x02,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00, + 0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x38,0x86,0x13,0x81,0xFE,0x64,0xC7,0x72, + 0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x38,0x76 + }; + + byte SZL_ID_01A0_IDX_0003[72] = { + 0xFF,0x09,0x00,0x44,0x01,0xA0,0x00,0x00,0x00,0x14,0x00,0x03,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00, + 0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x38,0x86,0x13,0x81,0xFE,0x64,0xC7,0x72, + 0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x38,0x76,0x43,0x01,0xFF,0x46,0xC7, + 0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x29,0x66 + }; + + byte SZL_ID_01A0_IDX_0004[92] = { + 0xFF,0x09,0x00,0x58,0x01,0xA0,0x00,0x00,0x00,0x14,0x00,0x04,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00, + 0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x38,0x86,0x13,0x81,0xFE,0x64,0xC7,0x72, + 0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x38,0x76,0x43,0x01,0xFF,0x46,0xC7, + 0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x29,0x66,0x43,0x04,0xFF,0x84, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04,0x23,0x52,0x13,0x90,0x16 + }; + + byte SZL_ID_01A0_IDX_0005[112] = { + 0xFF,0x09,0x00,0x6C,0x01,0xA0,0x00,0x00,0x00,0x14,0x00,0x05,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00, + 0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x38,0x86,0x13,0x81,0xFE,0x64,0xC7,0x72, + 0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x38,0x76,0x43,0x01,0xFF,0x46,0xC7, + 0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x29,0x66,0x43,0x04,0xFF,0x84, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04,0x23,0x52,0x13,0x90,0x16,0x43,0x02,0xFF, + 0x68,0xC7,0x00,0x00,0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x88,0x56 + }; + + byte SZL_ID_01A0_IDX_0006[132] = { + 0xFF,0x09,0x00,0x80,0x01,0xA0,0x00,0x00,0x00,0x14,0x00,0x06,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00, + 0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x38,0x86,0x13,0x81,0xFE,0x64,0xC7,0x72, + 0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x38,0x76,0x43,0x01,0xFF,0x46,0xC7, + 0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x29,0x66,0x43,0x04,0xFF,0x84, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04,0x23,0x52,0x13,0x90,0x16,0x43,0x02,0xFF, + 0x68,0xC7,0x00,0x00,0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x88,0x56,0x13,0x81, + 0xFE,0x64,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x88,0x46 + }; + + byte SZL_ID_01A0_IDX_0007[152] = { + 0xFF,0x09,0x00,0x94,0x01,0xA0,0x00,0x00,0x00,0x14,0x00,0x07,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00, + 0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x38,0x86,0x13,0x81,0xFE,0x64,0xC7,0x72, + 0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x38,0x76,0x43,0x01,0xFF,0x46,0xC7, + 0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x29,0x66,0x43,0x04,0xFF,0x84, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04,0x23,0x52,0x13,0x90,0x16,0x43,0x02,0xFF, + 0x68,0xC7,0x00,0x00,0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x88,0x56,0x13,0x81, + 0xFE,0x64,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x88,0x46,0x43, + 0x01,0xFF,0x46,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x79,0x46 + }; + + byte SZL_ID_01A0_IDX_0008[172] = { + 0xFF,0x09,0x00,0xA8,0x01,0xA0,0x00,0x00,0x00,0x14,0x00,0x08,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00, + 0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x38,0x86,0x13,0x81,0xFE,0x64,0xC7,0x72, + 0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x38,0x76,0x43,0x01,0xFF,0x46,0xC7, + 0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x29,0x66,0x43,0x04,0xFF,0x84, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04,0x23,0x52,0x13,0x90,0x16,0x43,0x02,0xFF, + 0x68,0xC7,0x00,0x00,0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x88,0x56,0x13,0x81, + 0xFE,0x64,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x88,0x46,0x43, + 0x01,0xFF,0x46,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x79,0x46, + 0x43,0x04,0xFF,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04,0x23,0x51,0x19,0x48, + 0x26 + }; + + byte SZL_ID_01A0_IDX_0009[192] = { + 0xFF,0x09,0x00,0xBC,0x01,0xA0,0x00,0x00,0x00,0x14,0x00,0x09,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00, + 0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x38,0x86,0x13,0x81,0xFE,0x64,0xC7,0x72, + 0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x38,0x76,0x43,0x01,0xFF,0x46,0xC7, + 0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x29,0x66,0x43,0x04,0xFF,0x84, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04,0x23,0x52,0x13,0x90,0x16,0x43,0x02,0xFF, + 0x68,0xC7,0x00,0x00,0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x88,0x56,0x13,0x81, + 0xFE,0x64,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x88,0x46,0x43, + 0x01,0xFF,0x46,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x79,0x46, + 0x43,0x04,0xFF,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04,0x23,0x51,0x19,0x48, + 0x26,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x50,0x45, + 0x09,0x96 + }; + + byte SZL_ID_01A0_IDX_000A[212] = { + 0xFF,0x09,0x00,0xD0,0x01,0xA0,0x00,0x00,0x00,0x14,0x00,0x0A,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00, + 0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x38,0x86,0x13,0x81,0xFE,0x64,0xC7,0x72, + 0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x38,0x76,0x43,0x01,0xFF,0x46,0xC7, + 0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x29,0x66,0x43,0x04,0xFF,0x84, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04,0x23,0x52,0x13,0x90,0x16,0x43,0x02,0xFF, + 0x68,0xC7,0x00,0x00,0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x88,0x56,0x13,0x81, + 0xFE,0x64,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x88,0x46,0x43, + 0x01,0xFF,0x46,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x79,0x46, + 0x43,0x04,0xFF,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04,0x23,0x51,0x19,0x48, + 0x26,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x50,0x45, + 0x09,0x96,0x13,0x81,0xFE,0x64,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x50, + 0x45,0x09,0x86 + }; + + byte SZL_ID_01A0_IDX_000B[232] = { + 0xFF,0x09,0x00,0xE4,0x01,0xA0,0x00,0x00,0x00,0x14,0x00,0x0B,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00, + 0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x38,0x86,0x13,0x81,0xFE,0x64,0xC7,0x72, + 0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x38,0x76,0x43,0x01,0xFF,0x46,0xC7, + 0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x29,0x66,0x43,0x04,0xFF,0x84, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04,0x23,0x52,0x13,0x90,0x16,0x43,0x02,0xFF, + 0x68,0xC7,0x00,0x00,0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x88,0x56,0x13,0x81, + 0xFE,0x64,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x88,0x46,0x43, + 0x01,0xFF,0x46,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x79,0x46, + 0x43,0x04,0xFF,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04,0x23,0x51,0x19,0x48, + 0x26,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x50,0x45, + 0x09,0x96,0x13,0x81,0xFE,0x64,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x50, + 0x45,0x09,0x86,0x43,0x01,0xFF,0x46,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23, + 0x50,0x45,0x00,0x96 + }; + + byte SZL_ID_01A0_IDX_000C[252] = { + 0xFF,0x09,0x00,0xF8,0x01,0xA0,0x00,0x00,0x00,0x14,0x00,0x0C,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00, + 0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x38,0x86,0x13,0x81,0xFE,0x64,0xC7,0x72, + 0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x38,0x76,0x43,0x01,0xFF,0x46,0xC7, + 0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x29,0x66,0x43,0x04,0xFF,0x84, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04,0x23,0x52,0x13,0x90,0x16,0x43,0x02,0xFF, + 0x68,0xC7,0x00,0x00,0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x88,0x56,0x13,0x81, + 0xFE,0x64,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x88,0x46,0x43, + 0x01,0xFF,0x46,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x79,0x46, + 0x43,0x04,0xFF,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04,0x23,0x51,0x19,0x48, + 0x26,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x50,0x45, + 0x09,0x96,0x13,0x81,0xFE,0x64,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x50, + 0x45,0x09,0x86,0x43,0x01,0xFF,0x46,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23, + 0x50,0x45,0x00,0x96,0x43,0x04,0xFF,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04, + 0x23,0x50,0x42,0x54,0x66 + }; + + byte SZL_ID_01A0_IDX_000D[272] = { + 0xFF,0x09,0x01,0x0C,0x01,0xA0,0x00,0x00,0x00,0x14,0x00,0x0D,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00, + 0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x38,0x86,0x13,0x81,0xFE,0x64,0xC7,0x72, + 0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x38,0x76,0x43,0x01,0xFF,0x46,0xC7, + 0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x29,0x66,0x43,0x04,0xFF,0x84, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04,0x23,0x52,0x13,0x90,0x16,0x43,0x02,0xFF, + 0x68,0xC7,0x00,0x00,0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x88,0x56,0x13,0x81, + 0xFE,0x64,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x88,0x46,0x43, + 0x01,0xFF,0x46,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x79,0x46, + 0x43,0x04,0xFF,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04,0x23,0x51,0x19,0x48, + 0x26,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x50,0x45, + 0x09,0x96,0x13,0x81,0xFE,0x64,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x50, + 0x45,0x09,0x86,0x43,0x01,0xFF,0x46,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23, + 0x50,0x45,0x00,0x96,0x43,0x04,0xFF,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04, + 0x23,0x50,0x42,0x54,0x66,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,0x00,0x08,0x14,0x77,0x14,0x94,0x02, + 0x04,0x23,0x50,0x32,0x40,0x46 + }; + + byte SZL_ID_01A0_IDX_000E[292] = { + 0xFF,0x09,0x01,0x20,0x01,0xA0,0x00,0x00,0x00,0x14,0x00,0x0E,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00, + 0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x38,0x86,0x13,0x81,0xFE,0x64,0xC7,0x72, + 0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x38,0x76,0x43,0x01,0xFF,0x46,0xC7, + 0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x29,0x66,0x43,0x04,0xFF,0x84, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04,0x23,0x52,0x13,0x90,0x16,0x43,0x02,0xFF, + 0x68,0xC7,0x00,0x00,0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x88,0x56,0x13,0x81, + 0xFE,0x64,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x88,0x46,0x43, + 0x01,0xFF,0x46,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x79,0x46, + 0x43,0x04,0xFF,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04,0x23,0x51,0x19,0x48, + 0x26,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x50,0x45, + 0x09,0x96,0x13,0x81,0xFE,0x64,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x50, + 0x45,0x09,0x86,0x43,0x01,0xFF,0x46,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23, + 0x50,0x45,0x00,0x96,0x43,0x04,0xFF,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04, + 0x23,0x50,0x42,0x54,0x66,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,0x00,0x08,0x14,0x77,0x14,0x94,0x02, + 0x04,0x23,0x50,0x32,0x40,0x46,0x13,0x81,0xFE,0x64,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94, + 0x02,0x04,0x23,0x50,0x32,0x40,0x36 + }; + + byte SZL_ID_01A0_IDX_000F[312] = { + 0xFF,0x09,0x01,0x34,0x01,0xA0,0x00,0x00,0x00,0x14,0x00,0x0F,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00, + 0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x38,0x86,0x13,0x81,0xFE,0x64,0xC7,0x72, + 0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x38,0x76,0x43,0x01,0xFF,0x46,0xC7, + 0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x29,0x66,0x43,0x04,0xFF,0x84, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04,0x23,0x52,0x13,0x90,0x16,0x43,0x02,0xFF, + 0x68,0xC7,0x00,0x00,0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x88,0x56,0x13,0x81, + 0xFE,0x64,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x88,0x46,0x43, + 0x01,0xFF,0x46,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x79,0x46, + 0x43,0x04,0xFF,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04,0x23,0x51,0x19,0x48, + 0x26,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x50,0x45, + 0x09,0x96,0x13,0x81,0xFE,0x64,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x50, + 0x45,0x09,0x86,0x43,0x01,0xFF,0x46,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23, + 0x50,0x45,0x00,0x96,0x43,0x04,0xFF,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04, + 0x23,0x50,0x42,0x54,0x66,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,0x00,0x08,0x14,0x77,0x14,0x94,0x02, + 0x04,0x23,0x50,0x32,0x40,0x46,0x13,0x81,0xFE,0x64,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94, + 0x02,0x04,0x23,0x50,0x32,0x40,0x36,0x43,0x01,0xFF,0x46,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14, + 0x94,0x02,0x04,0x23,0x50,0x32,0x31,0x46 + }; + + byte SZL_ID_01A0_IDX_0010[332] = { + 0xFF,0x09,0x01,0x48,0x01,0xA0,0x00,0x00,0x00,0x14,0x00,0x10,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00, + 0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x38,0x86,0x13,0x81,0xFE,0x64,0xC7,0x72, + 0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x38,0x76,0x43,0x01,0xFF,0x46,0xC7, + 0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x29,0x66,0x43,0x04,0xFF,0x84, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04,0x23,0x52,0x13,0x90,0x16,0x43,0x02,0xFF, + 0x68,0xC7,0x00,0x00,0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x88,0x56,0x13,0x81, + 0xFE,0x64,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x88,0x46,0x43, + 0x01,0xFF,0x46,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x79,0x46, + 0x43,0x04,0xFF,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04,0x23,0x51,0x19,0x48, + 0x26,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x50,0x45, + 0x09,0x96,0x13,0x81,0xFE,0x64,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x50, + 0x45,0x09,0x86,0x43,0x01,0xFF,0x46,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23, + 0x50,0x45,0x00,0x96,0x43,0x04,0xFF,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04, + 0x23,0x50,0x42,0x54,0x66,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,0x00,0x08,0x14,0x77,0x14,0x94,0x02, + 0x04,0x23,0x50,0x32,0x40,0x46,0x13,0x81,0xFE,0x64,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94, + 0x02,0x04,0x23,0x50,0x32,0x40,0x36,0x43,0x01,0xFF,0x46,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14, + 0x94,0x02,0x04,0x23,0x50,0x32,0x31,0x46,0x43,0x04,0xFF,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x94,0x02,0x04,0x23,0x50,0x29,0x92,0x26 + }; + + byte SZL_ID_01A0_IDX_0011[352] = { + 0xFF,0x09,0x01,0x5C,0x01,0xA0,0x00,0x00,0x00,0x14,0x00,0x11,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00, + 0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x38,0x86,0x13,0x81,0xFE,0x64,0xC7,0x72, + 0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x38,0x76,0x43,0x01,0xFF,0x46,0xC7, + 0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x29,0x66,0x43,0x04,0xFF,0x84, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04,0x23,0x52,0x13,0x90,0x16,0x43,0x02,0xFF, + 0x68,0xC7,0x00,0x00,0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x88,0x56,0x13,0x81, + 0xFE,0x64,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x88,0x46,0x43, + 0x01,0xFF,0x46,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x79,0x46, + 0x43,0x04,0xFF,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04,0x23,0x51,0x19,0x48, + 0x26,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x50,0x45, + 0x09,0x96,0x13,0x81,0xFE,0x64,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x50, + 0x45,0x09,0x86,0x43,0x01,0xFF,0x46,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23, + 0x50,0x45,0x00,0x96,0x43,0x04,0xFF,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04, + 0x23,0x50,0x42,0x54,0x66,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,0x00,0x08,0x14,0x77,0x14,0x94,0x02, + 0x04,0x23,0x50,0x32,0x40,0x46,0x13,0x81,0xFE,0x64,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94, + 0x02,0x04,0x23,0x50,0x32,0x40,0x36,0x43,0x01,0xFF,0x46,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14, + 0x94,0x02,0x04,0x23,0x50,0x32,0x31,0x46,0x43,0x04,0xFF,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x94,0x02,0x04,0x23,0x50,0x29,0x92,0x26,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,0x00,0x08,0x14, + 0x77,0x14,0x94,0x02,0x04,0x23,0x49,0x22,0x26,0x66 + }; + + byte SZL_ID_01A0_IDX_0012[372] = { + 0xFF,0x09,0x01,0x70,0x01,0xA0,0x00,0x00,0x00,0x14,0x00,0x12,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00, + 0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x38,0x86,0x13,0x81,0xFE,0x64,0xC7,0x72, + 0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x38,0x76,0x43,0x01,0xFF,0x46,0xC7, + 0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x29,0x66,0x43,0x04,0xFF,0x84, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04,0x23,0x52,0x13,0x90,0x16,0x43,0x02,0xFF, + 0x68,0xC7,0x00,0x00,0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x88,0x56,0x13,0x81, + 0xFE,0x64,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x88,0x46,0x43, + 0x01,0xFF,0x46,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x79,0x46, + 0x43,0x04,0xFF,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04,0x23,0x51,0x19,0x48, + 0x26,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x50,0x45, + 0x09,0x96,0x13,0x81,0xFE,0x64,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x50, + 0x45,0x09,0x86,0x43,0x01,0xFF,0x46,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23, + 0x50,0x45,0x00,0x96,0x43,0x04,0xFF,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04, + 0x23,0x50,0x42,0x54,0x66,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,0x00,0x08,0x14,0x77,0x14,0x94,0x02, + 0x04,0x23,0x50,0x32,0x40,0x46,0x13,0x81,0xFE,0x64,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94, + 0x02,0x04,0x23,0x50,0x32,0x40,0x36,0x43,0x01,0xFF,0x46,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14, + 0x94,0x02,0x04,0x23,0x50,0x32,0x31,0x46,0x43,0x04,0xFF,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x94,0x02,0x04,0x23,0x50,0x29,0x92,0x26,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,0x00,0x08,0x14, + 0x77,0x14,0x94,0x02,0x04,0x23,0x49,0x22,0x26,0x66,0x13,0x81,0xFE,0x64,0xC7,0x72,0x43,0x04,0x08, + 0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x49,0x22,0x26,0x56 + }; + + byte SZL_ID_01A0_IDX_0013[392] = { + 0xFF,0x09,0x01,0x84,0x01,0xA0,0x00,0x00,0x00,0x14,0x00,0x13,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00, + 0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x38,0x86,0x13,0x81,0xFE,0x64,0xC7,0x72, + 0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x38,0x76,0x43,0x01,0xFF,0x46,0xC7, + 0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x29,0x66,0x43,0x04,0xFF,0x84, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04,0x23,0x52,0x13,0x90,0x16,0x43,0x02,0xFF, + 0x68,0xC7,0x00,0x00,0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x88,0x56,0x13,0x81, + 0xFE,0x64,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x88,0x46,0x43, + 0x01,0xFF,0x46,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x79,0x46, + 0x43,0x04,0xFF,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04,0x23,0x51,0x19,0x48, + 0x26,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x50,0x45, + 0x09,0x96,0x13,0x81,0xFE,0x64,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x50, + 0x45,0x09,0x86,0x43,0x01,0xFF,0x46,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23, + 0x50,0x45,0x00,0x96,0x43,0x04,0xFF,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04, + 0x23,0x50,0x42,0x54,0x66,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,0x00,0x08,0x14,0x77,0x14,0x94,0x02, + 0x04,0x23,0x50,0x32,0x40,0x46,0x13,0x81,0xFE,0x64,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94, + 0x02,0x04,0x23,0x50,0x32,0x40,0x36,0x43,0x01,0xFF,0x46,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14, + 0x94,0x02,0x04,0x23,0x50,0x32,0x31,0x46,0x43,0x04,0xFF,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x94,0x02,0x04,0x23,0x50,0x29,0x92,0x26,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,0x00,0x08,0x14, + 0x77,0x14,0x94,0x02,0x04,0x23,0x49,0x22,0x26,0x66,0x13,0x81,0xFE,0x64,0xC7,0x72,0x43,0x04,0x08, + 0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x49,0x22,0x26,0x56,0x43,0x01,0xFF,0x46,0xC7,0x72,0x43,0x04, + 0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x49,0x22,0x17,0x66 + }; + + byte SZL_ID_01A0_IDX_0014[412] = { + 0xFF,0x09,0x01,0x98,0x01,0xA0,0x00,0x00,0x00,0x14,0x00,0x14,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00, + 0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x38,0x86,0x13,0x81,0xFE,0x64,0xC7,0x72, + 0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x38,0x76,0x43,0x01,0xFF,0x46,0xC7, + 0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x29,0x66,0x43,0x04,0xFF,0x84, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04,0x23,0x52,0x13,0x90,0x16,0x43,0x02,0xFF, + 0x68,0xC7,0x00,0x00,0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x88,0x56,0x13,0x81, + 0xFE,0x64,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x88,0x46,0x43, + 0x01,0xFF,0x46,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x79,0x46, + 0x43,0x04,0xFF,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04,0x23,0x51,0x19,0x48, + 0x26,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x50,0x45, + 0x09,0x96,0x13,0x81,0xFE,0x64,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x50, + 0x45,0x09,0x86,0x43,0x01,0xFF,0x46,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23, + 0x50,0x45,0x00,0x96,0x43,0x04,0xFF,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04, + 0x23,0x50,0x42,0x54,0x66,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,0x00,0x08,0x14,0x77,0x14,0x94,0x02, + 0x04,0x23,0x50,0x32,0x40,0x46,0x13,0x81,0xFE,0x64,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94, + 0x02,0x04,0x23,0x50,0x32,0x40,0x36,0x43,0x01,0xFF,0x46,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14, + 0x94,0x02,0x04,0x23,0x50,0x32,0x31,0x46,0x43,0x04,0xFF,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x94,0x02,0x04,0x23,0x50,0x29,0x92,0x26,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,0x00,0x08,0x14, + 0x77,0x14,0x94,0x02,0x04,0x23,0x49,0x22,0x26,0x66,0x13,0x81,0xFE,0x64,0xC7,0x72,0x43,0x04,0x08, + 0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x49,0x22,0x26,0x56,0x43,0x01,0xFF,0x46,0xC7,0x72,0x43,0x04, + 0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x49,0x22,0x17,0x66,0x43,0x04,0xFF,0x84,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04,0x23,0x49,0x19,0x75,0x46 + }; + + byte SZL_ID_01A0_IDX_0015[432] = { + 0xFF,0x09,0x01,0xAC,0x01,0xA0,0x00,0x00,0x00,0x14,0x00,0x15,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00, + 0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x38,0x86,0x13,0x81,0xFE,0x64,0xC7,0x72, + 0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x38,0x76,0x43,0x01,0xFF,0x46,0xC7, + 0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x29,0x66,0x43,0x04,0xFF,0x84, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04,0x23,0x52,0x13,0x90,0x16,0x43,0x02,0xFF, + 0x68,0xC7,0x00,0x00,0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x88,0x56,0x13,0x81, + 0xFE,0x64,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x88,0x46,0x43, + 0x01,0xFF,0x46,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x79,0x46, + 0x43,0x04,0xFF,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04,0x23,0x51,0x19,0x48, + 0x26,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x50,0x45, + 0x09,0x96,0x13,0x81,0xFE,0x64,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x50, + 0x45,0x09,0x86,0x43,0x01,0xFF,0x46,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23, + 0x50,0x45,0x00,0x96,0x43,0x04,0xFF,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04, + 0x23,0x50,0x42,0x54,0x66,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,0x00,0x08,0x14,0x77,0x14,0x94,0x02, + 0x04,0x23,0x50,0x32,0x40,0x46,0x13,0x81,0xFE,0x64,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94, + 0x02,0x04,0x23,0x50,0x32,0x40,0x36,0x43,0x01,0xFF,0x46,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14, + 0x94,0x02,0x04,0x23,0x50,0x32,0x31,0x46,0x43,0x04,0xFF,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x94,0x02,0x04,0x23,0x50,0x29,0x92,0x26,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,0x00,0x08,0x14, + 0x77,0x14,0x94,0x02,0x04,0x23,0x49,0x22,0x26,0x66,0x13,0x81,0xFE,0x64,0xC7,0x72,0x43,0x04,0x08, + 0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x49,0x22,0x26,0x56,0x43,0x01,0xFF,0x46,0xC7,0x72,0x43,0x04, + 0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x49,0x22,0x17,0x66,0x43,0x04,0xFF,0x84,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04,0x23,0x49,0x19,0x75,0x46,0x43,0x02,0xFF,0x68,0xC7,0x00, + 0x00,0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x48,0x32,0x21,0x16 + }; + + byte SZL_ID_0117_IDX_0000[16] = { + 0xFF,0x09,0x00,0x0C,0x01,0x17,0x00,0x00,0x00,0x04,0x00,0x01,0x00,0x00,0x00,0x01 + }; + + byte SZL_ID_0117_IDX_0001[16] = { + 0xFF,0x09,0x00,0x0C,0x01,0x17,0x00,0x01,0x00,0x04,0x00,0x01,0x00,0x01,0x00,0x03 + }; + + byte SZL_ID_0117_IDX_0002[16] = { + 0xFF,0x09,0x00,0x0C,0x01,0x17,0x00,0x02,0x00,0x04,0x00,0x01,0x00,0x02,0x00,0x02 + }; + + byte SZL_ID_0117_IDX_0003[16] = { + 0xFF,0x09,0x00,0x0C,0x01,0x17,0x00,0x03,0x00,0x04,0x00,0x01,0x00,0x03,0x00,0x01 + }; + + byte SZL_ID_0117_IDX_0004[16] = { + 0xFF,0x09,0x00,0x0C,0x01,0x17,0x00,0x04,0x00,0x04,0x00,0x01,0x00,0x04,0x00,0x01 + }; + + byte SZL_ID_0118_IDX_0000[16] = { + 0xFF,0x09,0x00,0x0C,0x01,0x18,0x00,0x00,0x00,0x04,0x00,0x01,0x00,0x00,0x00,0x08 + }; + + byte SZL_ID_0118_IDX_0001[16] = { + 0xFF,0x09,0x00,0x0C,0x01,0x18,0x00,0x01,0x00,0x04,0x00,0x01,0x00,0x01,0x00,0x08 + }; + + byte SZL_ID_0118_IDX_0002[16] = { + 0xFF,0x09,0x00,0x0C,0x01,0x18,0x00,0x02,0x00,0x04,0x00,0x01,0x00,0x02,0x00,0x08 + }; + + byte SZL_ID_0118_IDX_0003[16] = { + 0xFF,0x09,0x00,0x0C,0x01,0x18,0x00,0x03,0x00,0x04,0x00,0x01,0x00,0x03,0x00,0x08 + }; + + byte SZL_ID_0131_IDX_0001[52] = { + 0xFF,0x09,0x00,0x30,0x01,0x31,0x00,0x01,0x00,0x28,0x00,0x01,0x00,0x01, + 0x08,0x00, // PDU SIZE : We expose 2048 00F0 + 0x04,0x00, // Max Commections : We expose 1024 0010 + 0x00,0xB7,0x1B,0x00,0x00,0x02,0xDC,0x6C,0x05,0xF5,0xE1,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + }; + + byte SZL_ID_0131_IDX_0002[52] = { + 0xFF,0x09,0x00,0x30,0x01,0x31,0x00,0x02,0x00,0x28,0x00,0x01,0x00,0x02,0xBE,0xFD,0x4F,0x00,0x00, + 0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x3C,0x01,0x08,0x00,0x00,0x00,0x7D,0x00,0x00,0x05,0x03,0x0F, + 0x00,0x00,0x08,0x00,0x00,0x0C,0x00,0x0A,0x00,0x00,0x00,0x01,0x00,0x00 + }; + + byte SZL_ID_0131_IDX_0003[52] = { + 0xFF,0x09,0x00,0x30,0x01,0x31,0x00,0x03,0x00,0x28,0x00,0x01,0x00,0x03,0x7F,0xFC,0x83,0x01, + 0x00,0xF0, // Max size of consistently readable data (will be set = PDU size) + 0x00,0x10,0x00,0x01,0x02,0x09,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + }; + + byte SZL_ID_0131_IDX_0004[52] = { + 0xFF,0x09,0x00,0x30,0x01,0x31,0x00,0x04,0x00,0x28,0x00,0x01,0x00,0x04,0xFE,0x01,0x62,0x41,0x63, + 0x00,0x1E,0x00,0x10,0x10,0x10,0x04,0x02,0x00,0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + }; + + byte SZL_ID_0131_IDX_0005[52] = { + 0xFF,0x09,0x00,0x30,0x01,0x31,0x00,0x05,0x00,0x28,0x00,0x01,0x00,0x05,0x3E,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x10,0x01,0xF4,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + }; + + byte SZL_ID_0131_IDX_0006[52] = { + 0xFF,0x09,0x00,0x30,0x01,0x31,0x00,0x06,0x00,0x28,0x00,0x01,0x00,0x06,0xF3,0x00,0x00,0xF8,0x01, + 0xF0,0xFF,0x4E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x20, + 0x01,0x00,0x00,0x0E,0x00,0x4C,0xFF,0xFF,0x05,0xC0,0x00,0x00,0x00,0x00 + }; + + byte SZL_ID_0131_IDX_0007[52] = { + 0xFF,0x09,0x00,0x30,0x01,0x31,0x00,0x07,0x00,0x28,0x00,0x01,0x00,0x07,0x01,0x00,0x3F,0x00,0x20, + 0x01,0x01,0x00,0x00,0x00,0x01,0x08,0x01,0x08,0x01,0x08,0x20,0x08,0x02,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + }; + + byte SZL_ID_0131_IDX_0008[52] = { + 0xFF,0x09,0x00,0x30,0x01,0x31,0x00,0x08,0x00,0x28,0x00,0x01,0x00,0x08,0x70,0x03,0x70,0x02,0x70, + 0x02,0x40,0x32,0x40,0x32,0x40,0x32,0x40,0x64,0x40,0x32,0x40,0x32,0x40,0x64,0x40,0x64,0x40,0x64, + 0x40,0x64,0x40,0x64,0x40,0x64,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + }; + + byte SZL_ID_0131_IDX_0009[52] = { + 0xFF,0x09,0x00,0x30,0x01,0x31,0x00,0x09,0x00,0x28,0x00,0x01,0x00,0x09,0x04,0x06,0x01,0x00,0x01, + 0xF7,0x01,0xF7,0x00,0x01,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + }; + + byte SZL_ID_0C91_IDX_07FE[28] = { + 0xFF,0x09,0x00,0x18,0x0C,0x91,0x07,0xFE,0x00,0x10,0x00,0x01,0x00,0x00,0x02,0x02,0x07,0xFE,0xA7, + 0xC4,0xA7,0xC4,0x00,0x00,0xB4,0x02,0x00,0x11 + }; + + +#endif + diff --git a/utils/snap7_src/src/core/s7_isotcp.cpp b/utils/snap7_src/src/core/s7_isotcp.cpp new file mode 100644 index 0000000..3d2ba7d --- /dev/null +++ b/utils/snap7_src/src/core/s7_isotcp.cpp @@ -0,0 +1,541 @@ +/*=============================================================================| +| 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_isotcp.h" +//--------------------------------------------------------------------------- +TIsoTcpSocket::TIsoTcpSocket() +{ + RecvTimeout = 3000; // Some old equipments are a bit slow to answer.... + RemotePort = isoTcpPort; + // These fields should be $0000 and in any case RFC says that they are not considered. + // But some equipment...need a non zero value for the source reference. + DstRef = 0x0000; + SrcRef = 0x0100; + // PDU size requested + IsoPDUSize =1024; + IsoMaxFragments=MaxIsoFragments; + LastIsoError=0; +} +//--------------------------------------------------------------------------- +TIsoTcpSocket::~TIsoTcpSocket() +{ +} +//--------------------------------------------------------------------------- +int TIsoTcpSocket::CheckPDU(void *pPDU, u_char PduTypeExpected) +{ + PIsoHeaderInfo Info; + int Size; + ClrIsoError(); + if (pPDU!=0) + { + Info = PIsoHeaderInfo(pPDU); + Size = PDUSize(pPDU); + // Performs check + if (( Size<7 ) || ( Size>IsoPayload_Size ) || // Checks RFC 1006 header length + ( Info->HLengthPDUType!=PduTypeExpected)) // Checks PDU Type + return SetIsoError(errIsoInvalidPDU); + else + return noError; + } + else + return SetIsoError(errIsoNullPointer); +} +//--------------------------------------------------------------------------- +int TIsoTcpSocket::SetIsoError(int Error) +{ + LastIsoError = Error | LastTcpError; + return LastIsoError; +} +//--------------------------------------------------------------------------- +void TIsoTcpSocket::ClrIsoError() +{ + LastIsoError=0; + LastTcpError=0; +} +//--------------------------------------------------------------------------- +int TIsoTcpSocket::BuildControlPDU() +{ + int ParLen, IsoLen; + + ClrIsoError(); + FControlPDU.COTP.Params.PduSizeCode=0xC0; // code that identifies TPDU size + FControlPDU.COTP.Params.PduSizeLen =0x01; // 1 byte this field + switch(IsoPDUSize) + { + case 128: + FControlPDU.COTP.Params.PduSizeVal =0x07; + break; + case 256: + FControlPDU.COTP.Params.PduSizeVal =0x08; + break; + case 512: + FControlPDU.COTP.Params.PduSizeVal =0x09; + break; + case 1024: + FControlPDU.COTP.Params.PduSizeVal =0x0A; + break; + case 2048: + FControlPDU.COTP.Params.PduSizeVal =0x0B; + break; + case 4096: + FControlPDU.COTP.Params.PduSizeVal =0x0C; + break; + case 8192: + FControlPDU.COTP.Params.PduSizeVal =0x0D; + break; + default: + FControlPDU.COTP.Params.PduSizeVal =0x0B; // Our Default + }; + // Build TSAPs + FControlPDU.COTP.Params.TSAP[0]=0xC1; // code that identifies source TSAP + FControlPDU.COTP.Params.TSAP[1]=2; // source TSAP Len + FControlPDU.COTP.Params.TSAP[2]=(SrcTSap>>8) & 0xFF; // HI part + FControlPDU.COTP.Params.TSAP[3]=SrcTSap & 0xFF; // LO part + + FControlPDU.COTP.Params.TSAP[4]=0xC2; // code that identifies dest TSAP + FControlPDU.COTP.Params.TSAP[5]=2; // dest TSAP Len + FControlPDU.COTP.Params.TSAP[6]=(DstTSap>>8) & 0xFF; // HI part + FControlPDU.COTP.Params.TSAP[7]=DstTSap & 0xFF; // LO part + + // Params length + ParLen=11; // 2 Src TSAP (Code+field Len) + + // 2 Src TSAP len + + // 2 Dst TSAP (Code+field Len) + + // 2 Src TSAP len + + // 3 PDU size (Code+field Len+Val) = 11 + // Telegram length + IsoLen=sizeof(TTPKT)+ // TPKT Header + 7 + // COTP Header Size without params + ParLen; // COTP params + + FControlPDU.TPKT.Version =isoTcpVersion; + FControlPDU.TPKT.Reserved =0; + FControlPDU.TPKT.HI_Lenght=0; // Connection Telegram size cannot exced 255 bytes, so + // this field is always 0 + FControlPDU.TPKT.LO_Lenght=IsoLen; + + FControlPDU.COTP.HLength =ParLen + 6; // <-- 6 = 7 - 1 (COTP Header size - 1) + FControlPDU.COTP.PDUType =pdu_type_CR; // Connection Request + FControlPDU.COTP.DstRef =DstRef; // Destination reference + FControlPDU.COTP.SrcRef =SrcRef; // Source reference + FControlPDU.COTP.CO_R =0x00; // Class + Option : RFC0983 states that it must be always 0x40 + // but for some equipment (S7) must be 0 in disaccord of specifications !!! + return noError; +} +//--------------------------------------------------------------------------- +int TIsoTcpSocket::PDUSize(void *pPDU) +{ + return PIsoHeaderInfo(pPDU)->TPKT.HI_Lenght*256+PIsoHeaderInfo( pPDU )->TPKT.LO_Lenght; +} +//--------------------------------------------------------------------------- +void TIsoTcpSocket::IsoParsePDU(TIsoControlPDU pdu) +{ +// Currently we accept a connection with any kind of src/dst tsap +// Override to implement special filters. +} +//--------------------------------------------------------------------------- +int TIsoTcpSocket::IsoConfirmConnection(u_char PDUType) +{ + PIsoControlPDU CPDU = PIsoControlPDU(&PDU); + u_short TempRef; + + ClrIsoError(); + PDU.COTP.PDUType=PDUType; + // Exchange SrcRef<->DstRef, not strictly needed by COTP 8073 but S7PLC as client needs it. + TempRef=CPDU->COTP.DstRef; + CPDU->COTP.DstRef=CPDU->COTP.SrcRef; + CPDU->COTP.SrcRef=0x0100;//TempRef; + + return SendPacket(&PDU,PDUSize(&PDU)); +} +//--------------------------------------------------------------------------- +void TIsoTcpSocket::FragmentSkipped(int Size) +{ +// override for log purpose +} +//--------------------------------------------------------------------------- +int TIsoTcpSocket::isoConnect() +{ + pbyte TmpControlPDU; + PIsoControlPDU ControlPDU; + u_int Length; + int Result; + + // Build the default connection telegram + BuildControlPDU(); + ControlPDU =&FControlPDU; + + // Checks the format + Result =CheckPDU(ControlPDU, pdu_type_CR); + if (Result!=0) + return Result; + + Result =SckConnect(); + if (Result==noError) + { + // Calcs the length + Length =PDUSize(ControlPDU); + // Send connection telegram + SendPacket(ControlPDU, Length); + if (LastTcpError==0) + { + TmpControlPDU = pbyte(ControlPDU); + // Receives TPKT header (4 bytes) + RecvPacket(TmpControlPDU, sizeof(TTPKT)); + if (LastTcpError==0) + { + // Calc the packet length + Length =PDUSize(TmpControlPDU); + // Check if it fits in the buffer and if it's greater then TTPKT size + if ((Length<=sizeof(TIsoControlPDU)) && (Length>sizeof(TTPKT))) + { + // Points to COTP + TmpControlPDU+=sizeof(TTPKT); + Length -= sizeof(TTPKT); + // Receives remainin bytes 4 bytes after + RecvPacket(TmpControlPDU, Length); + if (LastTcpError==0) + { + // Finally checks the Connection Confirm telegram + Result =CheckPDU(ControlPDU, pdu_type_CC); + if (Result!=0) + LastIsoError=Result; + } + else + Result =SetIsoError(errIsoRecvPacket); + } + else + Result =SetIsoError(errIsoInvalidPDU); + } + else + Result =SetIsoError(errIsoRecvPacket); + // Flush buffer + if (Result!=0) + Purge(); + } + else + Result =SetIsoError(errIsoSendPacket); + + if (Result!=0) + SckDisconnect(); + } + return Result; +} +//--------------------------------------------------------------------------- +int TIsoTcpSocket::isoSendBuffer(void *Data, int Size) +{ + int Result; + u_int IsoSize; + + ClrIsoError(); + // Total Size = Size + Header Size + IsoSize =Size+DataHeaderSize; + // Checks the length + if ((IsoSize>0) && (IsoSize<=IsoFrameSize)) + { + // Builds the header + Result =0; + // TPKT + PDU.TPKT.Version = isoTcpVersion; + PDU.TPKT.Reserved = 0; + PDU.TPKT.HI_Lenght= (u_short(IsoSize)>> 8) & 0xFF; + PDU.TPKT.LO_Lenght= u_short(IsoSize) & 0xFF; + // COPT + PDU.COTP.HLength =sizeof(TCOTP_DT)-1; + PDU.COTP.PDUType =pdu_type_DT; + PDU.COTP.EoT_Num =pdu_EoT; + // Fill payload + if (Data!=0) // Data=null ==> use internal buffer PDU.Payload + memcpy(&PDU.Payload, Data, Size); + // Send over TCP/IP + SendPacket(&PDU, IsoSize); + + if (LastTcpError!=0) + Result =SetIsoError(errIsoSendPacket); + } + else + Result =SetIsoError(errIsoInvalidDataSize ); + return Result; +} +//--------------------------------------------------------------------------- +int TIsoTcpSocket::isoRecvBuffer(void *Data, int & Size) +{ + int Result; + + ClrIsoError(); + Size =0; + Result =isoRecvPDU(&PDU); + if (Result==0) + { + Size =PDUSize( &PDU )-DataHeaderSize; + if (Data!=0) // Data=NULL ==> a child will consume directly PDY.Payload + memcpy(Data, &PDU.Payload, Size); + } + return Result; +} +//--------------------------------------------------------------------------- +int TIsoTcpSocket::isoExchangeBuffer(void *Data, int &Size) +{ + int Result; + + ClrIsoError(); + Result =isoSendBuffer(Data, Size); + if (Result==0) + Result =isoRecvBuffer(Data, Size); + return Result; +} +//--------------------------------------------------------------------------- +bool TIsoTcpSocket::IsoPDUReady() +{ + ClrIsoError(); + return PacketReady(sizeof(TCOTP_DT)); +} +//--------------------------------------------------------------------------- +int TIsoTcpSocket::isoDisconnect(bool OnlyTCP) +{ + int Result; + + ClrIsoError(); + if (Connected) + Purge(); // Flush pending + LastIsoError=0; + // OnlyTCP true -> Disconnect Request telegram is not required : only TCP disconnection + if (!OnlyTCP) + { + // if we are connected -> we have a valid connection telegram + if (Connected) + FControlPDU.COTP.PDUType =pdu_type_DR; + // Checks the format + Result =CheckPDU(&FControlPDU, pdu_type_DR); + if (Result!=0) + return Result; + // Sends Disconnect request + SendPacket(&FControlPDU, PDUSize(&FControlPDU)); + if (LastTcpError!=0) + { + Result =SetIsoError(errIsoSendPacket); + return Result; + } + } + // TCP disconnect + SckDisconnect(); + if (LastTcpError!=0) + Result =SetIsoError(errIsoDisconnect); + else + Result =0; + + return Result; +} +//--------------------------------------------------------------------------- +int TIsoTcpSocket::isoSendPDU(PIsoDataPDU Data) +{ + int Result; + + ClrIsoError(); + Result=CheckPDU(Data,pdu_type_DT); + if (Result==0) + { + SendPacket(Data,PDUSize(Data)); + if (LastTcpError!=0) + Result=SetIsoError(errIsoSendPacket); + } + return Result; +} +//------------------------------------------------------------------------------ +int TIsoTcpSocket::isoRecvFragment(void *From, int Max, int &Size, bool &EoT) +{ + int DataLength; + + Size =0; + EoT =false; + byte PDUType; + ClrIsoError(); + // header is received always from beginning + RecvPacket(&PDU, DataHeaderSize); // TPKT + COPT_DT + if (LastTcpError==0) + { + PDUType=PDU.COTP.PDUType; + switch (PDUType) + { + case pdu_type_CR: + case pdu_type_DR: + EoT=true; + break; + case pdu_type_DT: + EoT = (PDU.COTP.EoT_Num & 0x80) == 0x80; // EoT flag + break; + default: + return SetIsoError(errIsoInvalidPDU); + } + + DataLength = PDUSize(&PDU) - DataHeaderSize; + if (CheckPDU(&PDU, PDUType)!=0) + return LastIsoError; + // Checks for data presence + if (DataLength>0) // payload present + { + // Check if the data fits in the buffer + if(DataLength<=Max) + { + RecvPacket(From, DataLength); + if (LastTcpError!=0) + return SetIsoError(errIsoRecvPacket); + else + Size =DataLength; + } + else + return SetIsoError(errIsoPduOverflow); + } + } + else + return SetIsoError(errIsoRecvPacket); + + return LastIsoError; +} +//--------------------------------------------------------------------------- +// Fragments Recv schema +//------------------------------------------------------------------------------ +// +// packet 1 packet 2 packet 3 +// +--------+------------+ +--------+------------+ +--------+------------+ +// | HEADER | FRAGMENT 1 | | HEADER | FRAGMENT 2 | | HEADER | FRAGMENT 3 | +// +--------+------------+ +--------+------------+ +--------+------------+ +// | | | +// | +-----------+ | +// | | | +// | | +------------------------+ +// | | | (Packet 3 has EoT Flag set) +// V V V +// +--------+------------+------------+------------+ +// | HEADER | FRAGMENT 1 : FRAGMENT 2 : FRAGMENT 3 | +// +--------+------------+------------+------------+ +// ^ +// | +// +-- A new header is built with updated info +// +//------------------------------------------------------------------------------ +int TIsoTcpSocket::isoRecvPDU(PIsoDataPDU Data) +{ + int Result; + int Size; + pbyte pData; + int max; + int Offset; + int Received; + int NumParts; + bool Complete; + + NumParts =1; + Offset =0; + Complete =false; + ClrIsoError(); + pData = pbyte(&PDU.Payload); + do { + pData=pData+Offset; + max =IsoPayload_Size-Offset; // Maximum packet allowed + if (max>0) + { + Result =isoRecvFragment(pData, max, Received, Complete); + if((Result==0) && !Complete) + { + ++NumParts; + Offset += Received; + if (NumParts>IsoMaxFragments) + Result =SetIsoError(errIsoTooManyFragments); + } + } + else + Result =SetIsoError(errIsoTooManyFragments); + } while ((!Complete) && (Result==0)); + + + if (Result==0) + { + // Add to offset the header size + Size =Offset+Received+DataHeaderSize; + // Adjust header + PDU.TPKT.HI_Lenght =(u_short(Size)>>8) & 0xFF; + PDU.TPKT.LO_Lenght =u_short(Size) & 0xFF; + // Copies data if target is not the local PDU + if (Data!=&PDU) + memcpy(Data, &PDU, Size); + } + else + if (LastTcpError!=WSAECONNRESET) + Purge(); + return Result; +} +//--------------------------------------------------------------------------- +int TIsoTcpSocket::isoExchangePDU(PIsoDataPDU Data) +{ + int Result; + ClrIsoError(); + Result=isoSendPDU(Data); + if (Result==0) + Result=isoRecvPDU(Data); + return Result; +} +//--------------------------------------------------------------------------- +void TIsoTcpSocket::IsoPeek(void *pPDU, TPDUKind &PduKind) +{ + PIsoHeaderInfo Info; + u_int IsoLen; + + Info=PIsoHeaderInfo(pPDU); + IsoLen=PDUSize(Info); + + // Check for empty fragment : size of PDU = size of header and nothing else + if (IsoLen==DataHeaderSize ) + { + // We don't need to check the EoT flag since the PDU is empty.... + PduKind=pkEmptyFragment; + return; + }; + // Check for invalid packet : size of PDU < size of header + if (IsoLenDataHeaderSize : check the PDUType + switch (Info->PDUType) + { + case pdu_type_CR: + PduKind=pkConnectionRequest; + break; + case pdu_type_DR: + PduKind=pkDisconnectRequest; + break; + case pdu_type_DT: + PduKind=pkValidData; + break; + default: + PduKind=pkUnrecognizedType; + }; +} + + + diff --git a/utils/snap7_src/src/core/s7_isotcp.h b/utils/snap7_src/src/core/s7_isotcp.h new file mode 100644 index 0000000..e02a365 --- /dev/null +++ b/utils/snap7_src/src/core/s7_isotcp.h @@ -0,0 +1,271 @@ +/*=============================================================================| +| 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/ | +|=============================================================================*/ +#ifndef s7_isotcp_h +#define s7_isotcp_h +//--------------------------------------------------------------------------- +#include "snap_msgsock.h" +//--------------------------------------------------------------------------- +#pragma pack(1) + +#define isoTcpVersion 3 // RFC 1006 +#define isoTcpPort 102 // RFC 1006 +#define isoInvalidHandle 0 +#define MaxTSAPLength 16 // Max Lenght for Src and Dst TSAP +#define MaxIsoFragments 64 // Max fragments +#define IsoPayload_Size 4096 // Iso telegram Buffer size + +#define noError 0 + +const longword errIsoMask = 0x000F0000; +const longword errIsoBase = 0x0000FFFF; + +const longword errIsoConnect = 0x00010000; // Connection error +const longword errIsoDisconnect = 0x00020000; // Disconnect error +const longword errIsoInvalidPDU = 0x00030000; // Bad format +const longword errIsoInvalidDataSize = 0x00040000; // Bad Datasize passed to send/recv : buffer is invalid +const longword errIsoNullPointer = 0x00050000; // Null passed as pointer +const longword errIsoShortPacket = 0x00060000; // A short packet received +const longword errIsoTooManyFragments = 0x00070000; // Too many packets without EoT flag +const longword errIsoPduOverflow = 0x00080000; // The sum of fragments data exceded maximum packet size +const longword errIsoSendPacket = 0x00090000; // An error occurred during send +const longword errIsoRecvPacket = 0x000A0000; // An error occurred during recv +const longword errIsoInvalidParams = 0x000B0000; // Invalid TSAP params +const longword errIsoResvd_1 = 0x000C0000; // Unassigned +const longword errIsoResvd_2 = 0x000D0000; // Unassigned +const longword errIsoResvd_3 = 0x000E0000; // Unassigned +const longword errIsoResvd_4 = 0x000F0000; // Unassigned + +const longword ISO_OPT_TCP_NODELAY = 0x00000001; // Disable Nagle algorithm +const longword ISO_OPT_INSIDE_MTU = 0x00000002; // Max packet size < MTU ethernet card + +// TPKT Header - ISO on TCP - RFC 1006 (4 bytes) +typedef struct{ + u_char Version; // Always 3 for RFC 1006 + u_char Reserved; // 0 + u_char HI_Lenght; // High part of packet lenght (entire frame, payload and TPDU included) + u_char LO_Lenght; // Low part of packet lenght (entire frame, payload and TPDU included) +} TTPKT; // Packet length : min 7 max 65535 + +typedef struct { + u_char PduSizeCode; + u_char PduSizeLen; + u_char PduSizeVal; + u_char TSAP[245]; // We don't know in advance these fields.... +} TCOPT_Params ; + +// PDU Type constants - ISO 8073, not all are mentioned in RFC 1006 +// For our purposes we use only those labeled with ** +// These constants contains 4 low bit order 0 (credit nibble) +// +// $10 ED : Expedited Data +// $20 EA : Expedited Data Ack +// $40 UD : CLTP UD +// $50 RJ : Reject +// $70 AK : Ack data +// ** $80 DR : Disconnect request (note : S7 doesn't use it) +// ** $C0 DC : Disconnect confirm (note : S7 doesn't use it) +// ** $D0 CC : Connection confirm +// ** $E0 CR : Connection request +// ** $F0 DT : Data +// + +// COTP Header for CONNECTION REQUEST/CONFIRM - DISCONNECT REQUEST/CONFIRM +typedef struct { + u_char HLength; // Header length : initialized to 6 (length without params - 1) + // descending classes that add values in params field must update it. + u_char PDUType; // 0xE0 Connection request + // 0xD0 Connection confirm + // 0x80 Disconnect request + // 0xDC Disconnect confirm + u_short DstRef; // Destination reference : Always 0x0000 + u_short SrcRef; // Source reference : Always 0x0000 + u_char CO_R; // If the telegram is used for Connection request/Confirm, + // the meaning of this field is CLASS+OPTION : + // Class (High 4 bits) + Option (Low 4 bits) + // Class : Always 4 (0100) but is ignored in input (RFC States this) + // Option : Always 0, also this in ignored. + // If the telegram is used for Disconnect request, + // the meaning of this field is REASON : + // 1 Congestion at TSAP + // 2 Session entity not attached to TSAP + // 3 Address unknown (at TCP connect time) + // 128+0 Normal disconnect initiated by the session + // entity. + // 128+1 Remote transport entity congestion at connect + // request time + // 128+3 Connection negotiation failed + // 128+5 Protocol Error + // 128+8 Connection request refused on this network + // connection + // Parameter data : depending on the protocol implementation. + // ISO 8073 define several type of parameters, but RFC 1006 recognizes only + // TSAP related parameters and PDU size. See RFC 0983 for more details. + TCOPT_Params Params; + /* Other params not used here, list only for completeness + ACK_TIME = 0x85, 1000 0101 Acknowledge Time + RES_ERROR = 0x86, 1000 0110 Residual Error Rate + PRIORITY = 0x87, 1000 0111 Priority + TRANSIT_DEL = 0x88, 1000 1000 Transit Delay + THROUGHPUT = 0x89, 1000 1001 Throughput + SEQ_NR = 0x8A, 1000 1010 Subsequence Number (in AK) + REASSIGNMENT = 0x8B, 1000 1011 Reassignment Time + FLOW_CNTL = 0x8C, 1000 1100 Flow Control Confirmation (in AK) + TPDU_SIZE = 0xC0, 1100 0000 TPDU Size + SRC_TSAP = 0xC1, 1100 0001 TSAP-ID / calling TSAP ( in CR/CC ) + DST_TSAP = 0xC2, 1100 0010 TSAP-ID / called TSAP + CHECKSUM = 0xC3, 1100 0011 Checksum + VERSION_NR = 0xC4, 1100 0100 Version Number + PROTECTION = 0xC5, 1100 0101 Protection Parameters (user defined) + OPT_SEL = 0xC6, 1100 0110 Additional Option Selection + PROTO_CLASS = 0xC7, 1100 0111 Alternative Protocol Classes + PREF_MAX_TPDU_SIZE = 0xF0, 1111 0000 + INACTIVITY_TIMER = 0xF2, 1111 0010 + ADDICC = 0xe0 1110 0000 Additional Information on Connection Clearing + */ +} TCOTP_CO ; +typedef TCOTP_CO *PCOTP_CO; + +// COTP Header for DATA EXCHANGE +typedef struct { + u_char HLength; // Header length : 3 for this header + u_char PDUType; // 0xF0 for this header + u_char EoT_Num; // EOT (bit 7) + PDU Number (bits 0..6) + // EOT = 1 -> End of Trasmission Packet (This packet is complete) + // PDU Number : Always 0 +} TCOTP_DT; +typedef TCOTP_DT *PCOTP_DT; + +// Info part of a PDU, only common parts. We use it to check the consistence +// of a telegram regardless of it's nature (control or data). +typedef struct { + TTPKT TPKT; // TPKT Header + // Common part of any COTP + u_char HLength; // Header length : 3 for this header + u_char PDUType; // Pdu type +} TIsoHeaderInfo ; +typedef TIsoHeaderInfo *PIsoHeaderInfo; + +// PDU Type consts (Code + Credit) +const byte pdu_type_CR = 0xE0; // Connection request +const byte pdu_type_CC = 0xD0; // Connection confirm +const byte pdu_type_DR = 0x80; // Disconnect request +const byte pdu_type_DC = 0xC0; // Disconnect confirm +const byte pdu_type_DT = 0xF0; // Data transfer + +const byte pdu_EoT = 0x80; // End of Trasmission Packet (This packet is complete) + +const longword DataHeaderSize = sizeof(TTPKT)+sizeof(TCOTP_DT); +const longword IsoFrameSize = IsoPayload_Size+DataHeaderSize; + +typedef struct { + TTPKT TPKT; // TPKT Header + TCOTP_CO COTP; // COPT Header for CONNECTION stuffs +} TIsoControlPDU; +typedef TIsoControlPDU *PIsoControlPDU; + +typedef u_char TIsoPayload[IsoPayload_Size]; + +typedef struct { + TTPKT TPKT; // TPKT Header + TCOTP_DT COTP; // COPT Header for DATA EXCHANGE + TIsoPayload Payload; // Payload +} TIsoDataPDU ; + +typedef TIsoDataPDU *PIsoDataPDU; +typedef TIsoPayload *PIsoPayload; + +typedef enum { + pkConnectionRequest, + pkDisconnectRequest, + pkEmptyFragment, + pkInvalidPDU, + pkUnrecognizedType, + pkValidData +} TPDUKind ; + +#pragma pack() + +void ErrIsoText(int Error, char *Msg, int len); + +class TIsoTcpSocket : public TMsgSocket +{ +private: + + TIsoControlPDU FControlPDU; + int IsoMaxFragments; // max fragments allowed for an ISO telegram + // Checks the PDU format + int CheckPDU(void *pPDU, u_char PduTypeExpected); + // Receives the next fragment + int isoRecvFragment(void *From, int Max, int &Size, bool &EoT); +protected: + TIsoDataPDU PDU; + int SetIsoError(int Error); + // Builds the control PDU starting from address properties + virtual int BuildControlPDU(); + // Calcs the PDU size + int PDUSize(void *pPDU); + // Parses the connection request PDU to extract TSAP and PDU size info + virtual void IsoParsePDU(TIsoControlPDU PDU); + // Confirms the connection, override this method for special pourpose + // By default it checks the PDU format and resend it changing the pdu type + int IsoConfirmConnection(u_char PDUType); + void ClrIsoError(); + virtual void FragmentSkipped(int Size); +public: + word SrcTSap; // Source TSAP + word DstTSap; // Destination TSAP + word SrcRef; // Source Reference + word DstRef; // Destination Reference + int IsoPDUSize; + int LastIsoError; + //-------------------------------------------------------------------------- + TIsoTcpSocket(); + ~TIsoTcpSocket(); + // HIGH Level functions (work on payload hiding the underlying protocol) + // Connects with a peer, the connection PDU is automatically built starting from address scheme (see below) + int isoConnect(); + // Disconnects from a peer, if OnlyTCP = true, only a TCP disconnect is performed, + // otherwise a disconnect PDU is built and send. + int isoDisconnect(bool OnlyTCP); + // Sends a buffer, a valid header is created + int isoSendBuffer(void *Data, int Size); + // Receives a buffer + int isoRecvBuffer(void *Data, int & Size); + // Exchange cycle send->receive + int isoExchangeBuffer(void *Data, int & Size); + // A PDU is ready (at least its header) to be read + bool IsoPDUReady(); + // Same as isoSendBuffer, but the entire PDU has to be provided (in any case a check is performed) + int isoSendPDU(PIsoDataPDU Data); + // Same as isoRecvBuffer, but it returns the entire PDU, automatically enques the fragments + int isoRecvPDU(PIsoDataPDU Data); + // Same as isoExchangeBuffer, but the entire PDU has to be provided (in any case a check is performed) + int isoExchangePDU(PIsoDataPDU Data); + // Peeks an header info to know which kind of telegram is incoming + void IsoPeek(void *pPDU, TPDUKind &PduKind); +}; + +#endif // s7_isotcp_h diff --git a/utils/snap7_src/src/core/s7_micro_client.cpp b/utils/snap7_src/src/core/s7_micro_client.cpp new file mode 100644 index 0000000..bf247e5 --- /dev/null +++ b/utils/snap7_src/src/core/s7_micro_client.cpp @@ -0,0 +1,3328 @@ +/*=============================================================================| +| 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_micro_client.h" +//--------------------------------------------------------------------------- + +TSnap7MicroClient::TSnap7MicroClient() +{ + SrcRef =0x0100; // RFC0983 states that SrcRef and DetRef should be 0 + // and, in any case, they are ignored. + // S7 instead requires a number != 0 + // Libnodave uses 0x0100 + // S7Manager uses 0x0D00 + // TIA Portal V12 uses 0x1D00 + // WinCC uses 0x0300 + // Seems that every non zero value is good enough... + DstRef =0x0000; + SrcTSap =0x0100; + DstTSap =0x0000; // It's filled by connection functions + ConnectionType = CONNTYPE_PG; // Default connection type + memset(&Job,0,sizeof(TSnap7Job)); +} +//--------------------------------------------------------------------------- +TSnap7MicroClient::~TSnap7MicroClient() +{ + Destroying = true; +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::opReadArea() +{ + PReqFunReadParams ReqParams; + PResFunReadParams ResParams; + PS7ResHeader23 Answer; + PResFunReadItem ResData; + word RPSize; // ReqParams size + int WordSize; + uintptr_t Offset; + pbyte Target; + int Address; + int IsoSize; + int Start; + int MaxElements; // Max elements that we can transfer in a PDU + word NumElements; // Num of elements that we are asking for this telegram + int TotElements; // Total elements requested + int Size; + int Result; + + WordSize=DataSizeByte(Job.WordLen); // The size in bytes of an element that we are asking for + if (WordSize==0) + return errCliInvalidWordLen; + // First check : params bounds + if ((Job.Number<0) || (Job.Number>65535) || (Job.Start<0) || (Job.Amount<1)) + return errCliInvalidParams; + // Second check : transport size + if ((Job.WordLen==S7WLBit) && (Job.Amount>1)) + return errCliInvalidTransportSize; + // Request Params size + RPSize =sizeof(TReqFunReadItem)+2; // 1 item + FunRead + ItemsCount + // Setup pointers (note : PDUH_out and PDU.Payload are the same pointer) + ReqParams =PReqFunReadParams(pbyte(PDUH_out)+sizeof(TS7ReqHeader)); + Answer =PS7ResHeader23(&PDU.Payload); + ResParams =PResFunReadParams(pbyte(Answer)+ResHeaderSize23); + ResData =PResFunReadItem(pbyte(ResParams)+sizeof(TResFunReadParams)); + // Each packet cannot exceed the PDU length (in bytes) negotiated, and moreover + // we must ensure to transfer a "finite" number of item per PDU + MaxElements=(PDULength-sizeof(TS7ResHeader23)-sizeof(TResFunReadParams)-4) / WordSize; + TotElements=Job.Amount; + Start =Job.Start; + Offset =0; + Result =0; + while ((TotElements>0) && (Result==0)) + { + NumElements=TotElements; + if (NumElements>MaxElements) + NumElements=MaxElements; + + Target=pbyte(Job.pData)+Offset; + //----------------------------------------------- Read next slice----- + PDUH_out->P = 0x32; // Always 0x32 + PDUH_out->PDUType = PduType_request; // 0x01 + PDUH_out->AB_EX = 0x0000; // Always 0x0000 + PDUH_out->Sequence = GetNextWord(); // AutoInc + PDUH_out->ParLen = SwapWord(RPSize); // 14 bytes params + PDUH_out->DataLen = 0x0000; // No data + + ReqParams->FunRead = pduFuncRead; // 0x04 + ReqParams->ItemsCount = 1; + ReqParams->Items[0].ItemHead[0] = 0x12; + ReqParams->Items[0].ItemHead[1] = 0x0A; + ReqParams->Items[0].ItemHead[2] = 0x10; + ReqParams->Items[0].TransportSize = Job.WordLen; + ReqParams->Items[0].Length = SwapWord(NumElements); + ReqParams->Items[0].Area = Job.Area; + if (Job.Area==S7AreaDB) + ReqParams->Items[0].DBNumber = SwapWord(Job.Number); + else + ReqParams->Items[0].DBNumber = 0x0000; + // Adjusts the offset + if ((Job.WordLen==S7WLBit) || (Job.WordLen==S7WLCounter) || (Job.WordLen==S7WLTimer)) + Address = Start; + else + Address = Start*8; + + ReqParams->Items[0].Address[2] = Address & 0x000000FF; + Address = Address >> 8; + ReqParams->Items[0].Address[1] = Address & 0x000000FF; + Address = Address >> 8; + ReqParams->Items[0].Address[0] = Address & 0x000000FF; + + IsoSize = sizeof(TS7ReqHeader)+RPSize; + Result = isoExchangeBuffer(0,IsoSize); + // Get Data + if (Result==0) // 1St level Iso + { + Size = 0; + // Item level error + if (ResData->ReturnCode==0xFF) // <-- 0xFF means Result OK + { + // Calcs data size in bytes + Size = SwapWord(ResData->DataLength); + // Adjust Size in accord of TransportSize + if ((ResData->TransportSize != TS_ResOctet) && (ResData->TransportSize != TS_ResReal) && (ResData->TransportSize != TS_ResBit)) + Size = Size >> 3; + memcpy(Target, &ResData->Data[0], Size); + } + else + Result = CpuError(ResData->ReturnCode); + Offset+=Size; + }; + //-------------------------------------------------------------------- + TotElements -= NumElements; + Start += NumElements*WordSize; + } + return Result; +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::opWriteArea() +{ + PReqFunWriteParams ReqParams; + PReqFunWriteDataItem ReqData; // only 1 item for WriteArea Function + PResFunWrite ResParams; + PS7ResHeader23 Answer; + word RPSize; // ReqParams size + word RHSize; // Request headers size + bool First = true; + pbyte Source; + pbyte Target; + int Address; + int IsoSize; + int WordSize; + word Size; + uintptr_t Offset = 0; + int Start; // where we are starting from for this telegram + int MaxElements; // Max elements that we can transfer in a PDU + word NumElements; // Num of elements that we are asking for this telegram + int TotElements; // Total elements requested + int Result = 0; + + WordSize=DataSizeByte(Job.WordLen); // The size in bytes of an element that we are pushing + if (WordSize==0) + return errCliInvalidWordLen; + // First check : params bounds + if ((Job.Number<0) || (Job.Number>65535) || (Job.Start<0) || (Job.Amount<1)) + return errCliInvalidParams; + // Second check : transport size + if ((Job.WordLen==S7WLBit) && (Job.Amount>1)) + return errCliInvalidTransportSize; + + RHSize =sizeof(TS7ReqHeader)+ // Request header + 2+ // FunWrite+ItemCount (of TReqFunWriteParams) + sizeof(TReqFunWriteItem)+// 1 item reference + 4; // ReturnCode+TransportSize+DataLength + RPSize =sizeof(TReqFunWriteItem)+2; + + // Setup pointers (note : PDUH_out and PDU.Payload are the same pointer) + ReqParams=PReqFunWriteParams(pbyte(PDUH_out)+sizeof(TS7ReqHeader)); + ReqData =PReqFunWriteDataItem(pbyte(ReqParams)+sizeof(TReqFunWriteItem)+2); // 2 = FunWrite+ItemsCount + Target =pbyte(ReqData)+4; // 4 = ReturnCode+TransportSize+DataLength + Answer =PS7ResHeader23(&PDU.Payload); + ResParams=PResFunWrite(pbyte(Answer)+ResHeaderSize23); + + // Each packet cannot exceed the PDU length (in bytes) negotiated, and moreover + // we must ensure to transfer a "finite" number of item per PDU + MaxElements=(PDULength-RHSize) / WordSize; + TotElements=Job.Amount; + Start =Job.Start; + while ((TotElements>0) && (Result==0)) + { + NumElements=TotElements; + if (NumElements>MaxElements) + NumElements=MaxElements; + Source=pbyte(Job.pData)+Offset; + + Size=NumElements * WordSize; + PDUH_out->P=0x32; // Always 0x32 + PDUH_out->PDUType=PduType_request; // 0x01 + PDUH_out->AB_EX=0x0000; // Always 0x0000 + PDUH_out->Sequence=GetNextWord(); // AutoInc + PDUH_out->ParLen =SwapWord(RPSize); // 14 bytes params + PDUH_out->DataLen =SwapWord(Size+4); + + ReqParams->FunWrite=pduFuncWrite; // 0x05 + ReqParams->ItemsCount=1; + ReqParams->Items[0].ItemHead[0]=0x12; + ReqParams->Items[0].ItemHead[1]=0x0A; + ReqParams->Items[0].ItemHead[2]=0x10; + ReqParams->Items[0].TransportSize=Job.WordLen; + ReqParams->Items[0].Length=SwapWord(NumElements); + ReqParams->Items[0].Area=Job.Area; + if (Job.Area==S7AreaDB) + ReqParams->Items[0].DBNumber=SwapWord(Job.Number); + else + ReqParams->Items[0].DBNumber=0x0000; + + // Adjusts the offset + if ((Job.WordLen==S7WLBit) || (Job.WordLen==S7WLCounter) || (Job.WordLen==S7WLTimer)) + Address=Start; + else + Address=Start*8; + + ReqParams->Items[0].Address[2]=Address & 0x000000FF; + Address=Address >> 8; + ReqParams->Items[0].Address[1]=Address & 0x000000FF; + Address=Address >> 8; + ReqParams->Items[0].Address[0]=Address & 0x000000FF; + + ReqData->ReturnCode=0x00; + + switch(Job.WordLen) + { + case S7WLBit: + ReqData->TransportSize=TS_ResBit; + break; + case S7WLInt: + case S7WLDInt: + ReqData->TransportSize=TS_ResInt; + break; + case S7WLReal: + ReqData->TransportSize=TS_ResReal; + break; + case S7WLChar : + case S7WLCounter: + case S7WLTimer: + ReqData->TransportSize=TS_ResOctet; + break; + default: + ReqData->TransportSize=TS_ResByte; + break; + }; + + if ((ReqData->TransportSize!=TS_ResOctet) && (ReqData->TransportSize!=TS_ResReal) && (ReqData->TransportSize!=TS_ResBit)) + ReqData->DataLength=SwapWord(Size*8); + else + ReqData->DataLength=SwapWord(Size); + + memcpy(Target, Source, Size); + IsoSize=RHSize + Size; + Result=isoExchangeBuffer(0,IsoSize); + + if (Result==0) // 1St check : Iso result + { + Result=CpuError(SwapWord(Answer->Error)); // 2nd level global error + if (Result==0) + { // 2th check : item error + if (ResParams->Data[0] == 0xFF) // <-- 0xFF means Result OK + Result=0; + else + // Now we check the error : if it's the first part we report the cpu error + // otherwise we warn that the function failed but some data were written + if (First) + Result=CpuError(ResParams->Data[0]); + else + Result=errCliPartialDataWritten; + }; + Offset+=Size; + }; + First=false; + + TotElements-=NumElements; + Start+=(NumElements*WordSize); + } + return Result; +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::opReadMultiVars() +{ + PS7DataItem Item; + PReqFunReadParams ReqParams; + PS7ResHeader23 Answer; + PResFunReadParams ResParams; + TResFunReadData ResData; + + word RPSize; // ReqParams size + uintptr_t Offset =0 ; + word Slice; + longword Address; + int IsoSize; + pbyte P; + int ItemsCount, c, Result; + + Item = PS7DataItem(Job.pData); + ItemsCount = Job.Amount; + + // Some useful initial check to detail the errors (Since S7 CPU always answers + // with $05 if (something is wrong in params) + if (ItemsCount>MaxVars) + return errCliTooManyItems; + + // Adjusts Word Length in case of timers and counters and clears results + for (c = 0; c < ItemsCount; c++) + { + Item->Result=0; + if (Item->Area==S7AreaCT) + Item->WordLen=S7WLCounter; + if (Item->Area==S7AreaTM) + Item->WordLen=S7WLTimer; + Item++; + }; + + // Let's build the PDU + RPSize = word(2 + ItemsCount * sizeof(TReqFunReadItem)); + ReqParams = PReqFunReadParams(pbyte(PDUH_out)+sizeof(TS7ReqHeader)); + Answer = PS7ResHeader23(&PDU.Payload); + ResParams = PResFunReadParams(pbyte(Answer)+ResHeaderSize23); + // Fill Header + PDUH_out->P=0x32; // Always 0x32 + PDUH_out->PDUType=PduType_request; // 0x01 + PDUH_out->AB_EX=0x0000; // Always 0x0000 + PDUH_out->Sequence=GetNextWord(); // AutoInc + PDUH_out->ParLen=SwapWord(RPSize); // Request params size + PDUH_out->DataLen=0x0000; // No data in output + + // Fill Params + ReqParams->FunRead=pduFuncRead; // 0x04 + ReqParams->ItemsCount=ItemsCount; + + Item = PS7DataItem(Job.pData); + for (c = 0; c < ItemsCount; c++) + { + ReqParams->Items[c].ItemHead[0]=0x12; + ReqParams->Items[c].ItemHead[1]=0x0A; + ReqParams->Items[c].ItemHead[2]=0x10; + + ReqParams->Items[c].TransportSize=Item->WordLen; + ReqParams->Items[c].Length=SwapWord(Item->Amount); + ReqParams->Items[c].Area=Item->Area; + // Automatically drops DBNumber if (Area is not DB + if (Item->Area==S7AreaDB) + ReqParams->Items[c].DBNumber=SwapWord(Item->DBNumber); + else + ReqParams->Items[c].DBNumber=0x0000; + // Adjusts the offset + if ((Item->WordLen==S7WLBit) || (Item->WordLen==S7WLCounter) || (Item->WordLen==S7WLTimer)) + Address=Item->Start; + else + Address=Item->Start*8; + // Builds the offset + ReqParams->Items[c].Address[2]=Address & 0x000000FF; + Address=Address >> 8; + ReqParams->Items[c].Address[1]=Address & 0x000000FF; + Address=Address >> 8; + ReqParams->Items[c].Address[0]=Address & 0x000000FF; + Item++; + }; + + IsoSize=RPSize+sizeof(TS7ReqHeader); + if (IsoSize>PDULength) + return errCliSizeOverPDU; + Result=isoExchangeBuffer(0,IsoSize); + + if (Result!=0) + return Result; + + // Function level error + if (Answer->Error!=0) + return CpuError(SwapWord(Answer->Error)); + + if (ResParams->ItemCount!=ItemsCount) + return errCliInvalidPlcAnswer; + + P=pbyte(ResParams)+sizeof(TResFunReadParams); + Item = PS7DataItem(Job.pData); + for (c = 0; c < ItemsCount; c++) + { + ResData[c] =PResFunReadItem(pbyte(P)+Offset); + Slice=0; + // Item level error + if (ResData[c]->ReturnCode==0xFF) // <-- 0xFF means Result OK + { + // Calcs data size in bytes + Slice=SwapWord(ResData[c]->DataLength); + // Adjust Size in accord of TransportSize + if ((ResData[c]->TransportSize != TS_ResOctet) && (ResData[c]->TransportSize != TS_ResReal) && (ResData[c]->TransportSize != TS_ResBit)) + Slice=Slice >> 3; + + memcpy(Item->pdata, ResData[c]->Data, Slice); + Item->Result=0; + } + else + Item->Result=CpuError(ResData[c]->ReturnCode); + + if ((Slice % 2)!=0) + Slice++; // Skip fill byte for Odd frame + + Offset+=(4+Slice); + Item++; + }; + return Result; +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::opWriteMultiVars() +{ + PS7DataItem Item; + PReqFunWriteParams ReqParams; + PResFunWrite ResParams; + TReqFunWriteData ReqData; + PS7ResHeader23 Answer; + pbyte P; + uintptr_t Offset; + longword Address; + int ItemsCount, c, IsoSize; + word RPSize; // ReqParams size + word Size; // Write data size + int WordSize, Result; + + Item = PS7DataItem(Job.pData); + ItemsCount = Job.Amount; + + // Some useful initial check to detail the errors (Since S7 CPU always answers + // with $05 if (something is wrong in params) + if (ItemsCount>MaxVars) + return errCliTooManyItems; + + // Adjusts Word Length in case of timers and counters and clears results + for (c = 0; c < ItemsCount; c++) + { + Item->Result=0; + if (Item->Area==S7AreaCT) + Item->WordLen=S7WLCounter; + if (Item->Area==S7AreaTM) + Item->WordLen=S7WLTimer; + Item++; + }; + + // Let's build the PDU : setup pointers + RPSize = word(2 + ItemsCount * sizeof(TReqFunWriteItem)); + ReqParams = PReqFunWriteParams(pbyte(PDUH_out)+sizeof(TS7ReqHeader)); + Answer = PS7ResHeader23(&PDU.Payload); + ResParams = PResFunWrite(pbyte(Answer)+ResHeaderSize23); + P=pbyte(ReqParams)+RPSize; + + // Fill Header + PDUH_out->P=0x32; // Always 0x32 + PDUH_out->PDUType=PduType_request; // 0x01 + PDUH_out->AB_EX=0x0000; // Always 0x0000 + PDUH_out->Sequence=GetNextWord(); // AutoInc + PDUH_out->ParLen=SwapWord(RPSize); // Request params size + + // Fill Params + ReqParams->FunWrite=pduFuncWrite; // 0x05 + ReqParams->ItemsCount=ItemsCount; + + Offset=0; + Item = PS7DataItem(Job.pData); + for (c = 0; c < ItemsCount; c++) + { + // Items Params + ReqParams->Items[c].ItemHead[0]=0x12; + ReqParams->Items[c].ItemHead[1]=0x0A; + ReqParams->Items[c].ItemHead[2]=0x10; + + ReqParams->Items[c].TransportSize=Item->WordLen; + ReqParams->Items[c].Length=SwapWord(Item->Amount); + ReqParams->Items[c].Area=Item->Area; + + if (Item->Area==S7AreaDB) + ReqParams->Items[c].DBNumber=SwapWord(Item->DBNumber); + else + ReqParams->Items[c].DBNumber=0x0000; + + // Adjusts the offset + if ((Item->WordLen==S7WLBit) || (Item->WordLen==S7WLCounter) || (Item->WordLen==S7WLTimer)) + Address=Item->Start; + else + Address=Item->Start*8; + // Builds the offset + ReqParams->Items[c].Address[2]=Address & 0x000000FF; + Address=Address >> 8; + ReqParams->Items[c].Address[1]=Address & 0x000000FF; + Address=Address >> 8; + ReqParams->Items[c].Address[0]=Address & 0x000000FF; + + // Items Data + ReqData[c]=PReqFunWriteDataItem(pbyte(P)+Offset); + ReqData[c]->ReturnCode=0x00; + + switch (Item->WordLen) + { + case S7WLBit : + ReqData[c]->TransportSize=TS_ResBit; + break; + case S7WLInt : + case S7WLDInt : + ReqData[c]->TransportSize=TS_ResInt; + break; + case S7WLReal : + ReqData[c]->TransportSize=TS_ResReal; + break; + case S7WLChar : + case S7WLCounter : + case S7WLTimer : ReqData[c]->TransportSize=TS_ResOctet; + break; + default : + ReqData[c]->TransportSize=TS_ResByte; // byte/word/dword etc. + break; + }; + + WordSize=DataSizeByte(Item->WordLen); + Size=Item->Amount * WordSize; + + if ((ReqData[c]->TransportSize!=TS_ResOctet) && (ReqData[c]->TransportSize!=TS_ResReal) && (ReqData[c]->TransportSize!=TS_ResBit)) + ReqData[c]->DataLength=SwapWord(Size*8); + else + ReqData[c]->DataLength=SwapWord(Size); + + memcpy(ReqData[c]->Data, Item->pdata, Size); + + if ((Size % 2) != 0 && (ItemsCount - c != 1)) + Size++; // Skip fill byte for Odd frame (except for the last one) + + Offset+=(4+Size); // next item + Item++; + }; + + PDUH_out->DataLen=SwapWord(word(Offset)); + + IsoSize=RPSize+sizeof(TS7ReqHeader)+int(Offset); + if (IsoSize>PDULength) + return errCliSizeOverPDU; + Result=isoExchangeBuffer(0,IsoSize); + + if (Result!=0) + return Result; + + // Function level error + if (Answer->Error!=0) + return CpuError(SwapWord(Answer->Error)); + + if (ResParams->ItemCount!=ItemsCount) + return errCliInvalidPlcAnswer; + + Item = PS7DataItem(Job.pData); + for (c = 0; c < ItemsCount; c++) + { + // Item level error + if (ResParams->Data[c]==0xFF) // <-- 0xFF means Result OK + Item->Result=0; + else + Item->Result=CpuError(ResParams->Data[c]); + Item++; + }; + return Result; +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::opListBlocks() +{ + PReqFunGetBlockInfo ReqParams; + PReqDataFunBlocks ReqData; + PResFunGetBlockInfo ResParams; + PDataFunListAll ResData; + PS7ResHeader17 Answer; + PS7BlocksList List; + int IsoSize, Result; + + // Setup pointers (note : PDUH_out and PDU.Payload are the same pointer) + ReqParams=PReqFunGetBlockInfo(pbyte(PDUH_out)+sizeof(TS7ReqHeader)); + ReqData =PReqDataFunBlocks(pbyte(ReqParams)+sizeof(TReqFunGetBlockInfo)); + Answer =PS7ResHeader17(&PDU.Payload); + ResParams=PResFunGetBlockInfo(pbyte(Answer)+ResHeaderSize17); + ResData =PDataFunListAll(pbyte(ResParams)+sizeof(TResFunGetBlockInfo)); + List =PS7BlocksList(Job.pData); + // Fill Header + PDUH_out->P=0x32; // Always 0x32 + PDUH_out->PDUType=PduType_userdata; // 0x07 + PDUH_out->AB_EX=0x0000; // Always 0x0000 + PDUH_out->Sequence=GetNextWord(); // AutoInc + PDUH_out->ParLen=SwapWord(sizeof(TReqFunGetBlockInfo)); // 8 bytes params + PDUH_out->DataLen=SwapWord(sizeof(TReqDataFunBlocks)); // 4 bytes data + // Fill params (mostly constants) + ReqParams->Head[0]=0x00; + ReqParams->Head[1]=0x01; + ReqParams->Head[2]=0x12; + ReqParams->Plen =0x04; + ReqParams->Uk =0x11; + ReqParams->Tg =grBlocksInfo; + ReqParams->SubFun =SFun_ListAll; + ReqParams->Seq =0x00; + // Fill data + ReqData[0] =0x0A; + ReqData[1] =0x00; + ReqData[2] =0x00; + ReqData[3] =0x00; + + IsoSize=sizeof(TS7ReqHeader)+sizeof(TReqFunGetBlockInfo)+sizeof(TReqDataFunBlocks); + Result=isoExchangeBuffer(0,IsoSize); + // Get Data + if (Result==0) + { + if (ResParams->ErrNo==0) + { + if (SwapWord(ResData->Length)!=28) + return errCliInvalidPlcAnswer; + + for (int c = 0; c < 7; c++) + { + switch (ResData->Blocks[c].BType) + { + case Block_OB: + List->OBCount=SwapWord(ResData->Blocks[c].BCount); + break; + case Block_DB: + List->DBCount=SwapWord(ResData->Blocks[c].BCount); + break; + case Block_SDB: + List->SDBCount=SwapWord(ResData->Blocks[c].BCount); + break; + case Block_FC: + List->FCCount=SwapWord(ResData->Blocks[c].BCount); + break; + case Block_SFC: + List->SFCCount=SwapWord(ResData->Blocks[c].BCount); + break; + case Block_FB: + List->FBCount=SwapWord(ResData->Blocks[c].BCount); + break; + case Block_SFB: + List->SFBCount=SwapWord(ResData->Blocks[c].BCount); + break; + } + } + } + else + Result=CpuError(SwapWord(ResParams->ErrNo)); + } + return Result; +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::opListBlocksOfType() +{ + PReqFunGetBlockInfo ReqParams; + PReqDataBlockOfType ReqData; + + PS7ResHeader17 Answer; + PResFunGetBlockInfo ResParams; + PDataFunGetBot ResData; + longword *PadData; + word *List; + bool First; + bool Done = false; + byte BlockType, In_Seq; + int Count, Last, IsoSize, Result; + int c, CThis; + word DataLength; + bool RoomError = false; + + BlockType=Job.Area; + List=(word*)(&opData); + // Setup pointers (note : PDUH_out and PDU.Payload are the same pointer) + ReqParams=PReqFunGetBlockInfo(pbyte(PDUH_out)+sizeof(TS7ReqHeader)); + Answer =PS7ResHeader17(&PDU.Payload); + ResParams=PResFunGetBlockInfo(pbyte(Answer)+ResHeaderSize17); + ResData =PDataFunGetBot(pbyte(ResParams)+sizeof(TResFunGetBlockInfo)); + // Get Data + First =true; + In_Seq=0x00; // first group sequence, next will come from PLC + Count =0; + Last =0; + do + { + //<--------------------------------------------------------- Get next slice + // Fill Header + PDUH_out->P=0x32; // Always 0x32 + PDUH_out->PDUType=PduType_userdata; // 0x07 + PDUH_out->AB_EX=0x0000; // Always 0x0000 + PDUH_out->Sequence=GetNextWord(); // AutoInc + if (First) + { + PDUH_out->ParLen=SwapWord(8); // 8 bytes params + PDUH_out->DataLen=SwapWord(6); // 6 bytes data + DataLength=14; + } + else + { + PDUH_out->ParLen=SwapWord(12); // 12 bytes params + PDUH_out->DataLen=SwapWord(4); // 4 bytes data + DataLength=16; + } + + // Fill params (mostly constants) + ReqParams->Head[0]=0x00; + ReqParams->Head[1]=0x01; + ReqParams->Head[2]=0x12; + + if (First) + ReqParams->Plen =0x04; + else + ReqParams->Plen =0x08; + + if (First) + ReqParams->Uk = 0x11; + else + ReqParams->Uk = 0x12; + + ReqParams->Tg =grBlocksInfo; + ReqParams->SubFun =SFun_ListBoT; + ReqParams->Seq =In_Seq; + + // Fill data + if (First) + { + // overlap resvd and error to avoid another struct... + ReqData =PReqDataBlockOfType(pbyte(ReqParams)+sizeof(TReqFunGetBlockInfo)); + ReqData->RetVal =0xFF; + ReqData->TSize =TS_ResOctet; + ReqData->Length =SwapWord(0x0002); + ReqData->Zero =0x30; // zero ascii '0' + ReqData->BlkType =BlockType; + } + else + { + PadData =(longword*)(pbyte(ReqParams)+sizeof(TReqFunGetBlockInfo)); + ReqData =PReqDataBlockOfType(pbyte(ReqParams)+sizeof(TReqFunGetBlockInfo)+4); + *PadData =0x00000000; + ReqData->RetVal =0x0A; + ReqData->TSize =0x00; + ReqData->Length =0x0000; + ReqData->Zero =0x00; + ReqData->BlkType =0x00; + }; + + IsoSize=sizeof(TS7ReqHeader)+DataLength; + Result=isoExchangeBuffer(0,IsoSize); + + if (Result==0) + { + if (ResParams->ErrNo==0) + { + if (ResData->RetVal==0xFF) + { + Done=((ResParams->Rsvd & 0xFF00) == 0); // Low order byte = 0x00 => the sequence is done + In_Seq=ResParams->Seq; // every next telegram must have this number + CThis=((SwapWord(ResData->DataLen) - 4 ) / 4) + 1; // Partial counter + for (c=0; c < CThis+1; c++) + { + *List=SwapWord(ResData->Items[c].BlockNum); + Last++; + List++; + if (Last==0x8000) + { + Done=true; + break; + }; + }; + Count+=CThis; // Total counter + List--; + } + else + Result=errCliItemNotAvailable; + } + else + Result=errCliItemNotAvailable; + }; + First=false; + //---------------------------------------------------------> Get next slice + } + while ((!Done) && (Result==0)); + + *Job.pAmount=0; + if (Result==0) + { + if (Count>Job.Amount) + { + Count=Job.Amount; + RoomError=true; + } + memcpy(Job.pData, &opData, Count*2); + *Job.pAmount=Count; + + if (RoomError) // Result==0 -> override if romerror + Result=errCliPartialDataRead; + }; + + return Result; +} +//--------------------------------------------------------------------------- +void TSnap7MicroClient::FillTime(word SiemensTime, char *PTime) +{ + // SiemensTime -> number of seconds after 1/1/1984 + // This is not S7 date and time but is used only internally for block info + time_t TheDate = (SiemensTime * 86400)+ DeltaSecs; + struct tm * timeinfo = localtime (&TheDate); + if (timeinfo!=NULL) { + strftime(PTime,11,"%Y/%m/%d",timeinfo); + } + else + *PTime='\0'; +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::opAgBlockInfo() +{ + PS7BlockInfo BlockInfo; + PReqFunGetBlockInfo ReqParams; + PReqDataBlockInfo ReqData; + PS7ResHeader17 Answer; + PResFunGetBlockInfo ResParams; + PResDataBlockInfo ResData; + byte BlockType; + int BlockNum, IsoSize, Result; + + BlockType=Job.Area; + BlockNum =Job.Number; + BlockInfo=PS7BlockInfo(Job.pData); + memset(BlockInfo,0,sizeof(TS7BlockInfo)); + // Setup pointers (note : PDUH_out and PDU.Payload are the same pointer) + ReqParams=PReqFunGetBlockInfo(pbyte(PDUH_out)+sizeof(TS7ReqHeader)); + ReqData =PReqDataBlockInfo(pbyte(ReqParams)+sizeof(TReqFunGetBlockInfo)); + Answer =PS7ResHeader17(&PDU.Payload); + ResParams=PResFunGetBlockInfo(pbyte(Answer)+ResHeaderSize17); + ResData =PResDataBlockInfo(pbyte(ResParams)+sizeof(TResFunGetBlockInfo)); + // Fill Header + PDUH_out->P=0x32; // Always 0x32 + PDUH_out->PDUType=PduType_userdata; // 0x07 + PDUH_out->AB_EX=0x0000; // Always 0x0000 + PDUH_out->Sequence=GetNextWord(); // AutoInc + PDUH_out->ParLen=SwapWord(sizeof(TReqFunGetBlockInfo)); // 8 bytes params + PDUH_out->DataLen=SwapWord(sizeof(TReqDataBlockInfo)); // 4 bytes data + // Fill params (mostly constants) + ReqParams->Head[0]=0x00; + ReqParams->Head[1]=0x01; + ReqParams->Head[2]=0x12; + ReqParams->Plen =0x04; + ReqParams->Uk =0x11; + ReqParams->Tg =grBlocksInfo; + ReqParams->SubFun =SFun_BlkInfo; + ReqParams->Seq =0x00; + // Fill data + ReqData->RetVal =0xFF; + ReqData->TSize =TS_ResOctet; + ReqData->DataLen =SwapWord(0x0008); + ReqData->BlkPrfx =0x30; + ReqData->BlkType =BlockType; + ReqData->A =0x41; + + ReqData->AsciiBlk[0]=(BlockNum / 10000)+0x30; + BlockNum=BlockNum % 10000; + ReqData->AsciiBlk[1]=(BlockNum / 1000)+0x30; + BlockNum=BlockNum % 1000; + ReqData->AsciiBlk[2]=(BlockNum / 100)+0x30; + BlockNum=BlockNum % 100; + ReqData->AsciiBlk[3]=(BlockNum / 10)+0x30; + BlockNum=BlockNum % 10; + ReqData->AsciiBlk[4]=(BlockNum / 1)+0x30; + + IsoSize=sizeof(TS7ReqHeader)+sizeof(TReqFunGetBlockInfo)+sizeof(TReqDataBlockInfo); + Result=isoExchangeBuffer(0,IsoSize); + + // Get Data + if (Result==0) + { + if (ResParams->ErrNo==0) + { + if (SwapWord(ResData->Length)<40) // 78 + return errCliInvalidPlcAnswer; + if (ResData->RetVal==0xFF) // <-- 0xFF means Result OK + { + //<----------------------------------------------Fill block info + BlockInfo->BlkType=ResData->SubBlkType; + BlockInfo->BlkNumber=SwapWord(ResData->BlkNumber); + BlockInfo->BlkLang=ResData->BlkLang; + BlockInfo->BlkFlags=ResData->BlkFlags; + BlockInfo->MC7Size=SwapWord(ResData->MC7Len); + BlockInfo->LoadSize=SwapDWord(ResData->LenLoadMem); + BlockInfo->LocalData=SwapWord(ResData->LocDataLen); + BlockInfo->SBBLength=SwapWord(ResData->SbbLen); + BlockInfo->CheckSum=SwapWord(ResData->BlkChksum); + BlockInfo->Version=ResData->Version; + memcpy(BlockInfo->Author, ResData->Author, 8); + memcpy(BlockInfo->Family,ResData->Family,8); + memcpy(BlockInfo->Header,ResData->Header,8); + FillTime(SwapWord(ResData->CodeTime_dy),BlockInfo->CodeDate); + FillTime(SwapWord(ResData->IntfTime_dy),BlockInfo->IntfDate); + //---------------------------------------------->Fill block info + } + else + Result=CpuError(ResData->RetVal); + } + else + Result=CpuError(SwapWord(ResParams->ErrNo)); + }; + return Result; +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::opDBGet() +{ + TS7BlockInfo BI; + void * usrPData; + int * usrPSize; + int Result, Room; + bool RoomError = false; + + // Stores user pointer + usrPData=Job.pData; + usrPSize=Job.pAmount; + Room =Job.Amount; + + // 1 Pass : Get block info + Job.Area=Block_DB; + Job.pData=&BI; + Result=opAgBlockInfo(); + + // 2 Pass : Read the whole (MC7Size bytes) DB. + if (Result==0) + { + // Check user space + if (BI.MC7Size>Room) + { + Job.Amount=Room; + RoomError=true; + } + else + Job.Amount =BI.MC7Size; + // The data is read even if the buffer is small (the error is reported). + // Imagine that we want to read only a small amount of data at the + // beginning of a DB regardless it's size.... + Job.Area =S7AreaDB; + Job.WordLen=S7WLByte; + Job.Start =0; + Job.pData =usrPData; + Result =opReadArea(); + if (Result==0) + *usrPSize=Job.Amount; + } + + if ((Result==0) && RoomError) + return errCliBufferTooSmall; + else + return Result; +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::opDBFill() +{ + TS7BlockInfo BI; + int Result; + // new op : get block info + Job.Op =s7opAgBlockInfo; + Job.Area =Block_DB; + Job.pData=&BI; + Result =opAgBlockInfo(); + // Restore original op + Job.Op =s7opDBFill; + // Fill internal buffer then write it + if (Result==0) + { + Job.Amount =BI.MC7Size; + Job.Area =S7AreaDB; + Job.WordLen=S7WLByte; + Job.Start =0; + memset(&opData, byte(Job.IParam), Job.Amount); + Job.pData =&opData; + Result =opWriteArea(); + } + return Result; +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::opUpload() +{ + PS7ResHeader23 Answer; + int IsoSize; + byte Upload_ID = 0; // not strictly needed, only to avoid warning + byte BlockType; + int BlockNum, BlockLength, Result; + bool Done, Full; // if full==true, the data will be compatible to full download function + uintptr_t Offset; + bool RoomError = false; + + BlockType=Job.Area; + BlockNum =Job.Number; + Full =Job.IParam==1; + // Setup Answer (is the same for all Upload pdus) + Answer= PS7ResHeader23(&PDU.Payload); + // Init sequence + Done =false; + Offset=0; + //<-------------------------------------------------------------StartUpload + PReqFunStartUploadParams ReqParams; + PResFunStartUploadParams ResParams; + // Setup pointers (note : PDUH_out and PDU.Payload are the same pointer) + ReqParams=PReqFunStartUploadParams(pbyte(PDUH_out)+sizeof(TS7ReqHeader)); + ResParams=PResFunStartUploadParams(pbyte(Answer)+ResHeaderSize23); + // Init Header + PDUH_out->P=0x32; // Always 0x32 + PDUH_out->PDUType=PduType_request; // 0x01 + PDUH_out->AB_EX=0x0000; // Always 0x0000 + PDUH_out->Sequence=GetNextWord(); // AutoInc + PDUH_out->ParLen=SwapWord(sizeof(TReqFunStartUploadParams));// params size + PDUH_out->DataLen=0x0000; // No data + // Init Params + ReqParams->FunSUpld=pduStartUpload; + ReqParams->Uk6[0]=0x00; + ReqParams->Uk6[1]=0x00; + ReqParams->Uk6[2]=0x00; + ReqParams->Uk6[3]=0x00; + ReqParams->Uk6[4]=0x00; + ReqParams->Uk6[5]=0x00; + ReqParams->Upload_ID=Upload_ID; // At begining is 0) we will put upload id incoming from plc + ReqParams->Len_1 =0x09; // 9 bytes from here + ReqParams->Prefix=0x5F; + ReqParams->BlkPrfx=0x30; // '0' + ReqParams->BlkType=BlockType; + // Block number + ReqParams->AsciiBlk[0]=(BlockNum / 10000)+0x30; + BlockNum=BlockNum % 10000; + ReqParams->AsciiBlk[1]=(BlockNum / 1000)+0x30; + BlockNum=BlockNum % 1000; + ReqParams->AsciiBlk[2]=(BlockNum / 100)+0x30; + BlockNum=BlockNum % 100; + ReqParams->AsciiBlk[3]=(BlockNum / 10)+0x30; + BlockNum=BlockNum % 10; + ReqParams->AsciiBlk[4]=(BlockNum / 1)+0x30; + ReqParams->A=0x41; // 'A' + + IsoSize=sizeof(TS7ReqHeader)+sizeof(TReqFunStartUploadParams); + Result=isoExchangeBuffer(0,IsoSize); + // Get Upload Infos (only ID now) + if (Result==0) + { + if (Answer->Error==0) + Upload_ID=ResParams->Upload_ID; + else + Result=CpuError(SwapWord(Answer->Error)); + }; + //------------------------------------------------------------->StartUpload + if (Result==0) + { + //<--------------------------------------------------------FirstUpload + PReqFunUploadParams ReqParams; + PResFunUploadParams ResParams; + PResFunUploadDataHeaderFirst ResDataHeader; + pbyte Source; + pbyte Target; + int Size; + + // Setup pointers (note : PDUH_out and PDU.Payload are the same pointer) + ReqParams=PReqFunUploadParams(pbyte(PDUH_out)+sizeof(TS7ReqHeader)); + // First upload pdu consists of params, block info header, data. + ResParams=PResFunUploadParams(pbyte(Answer)+ResHeaderSize23); + ResDataHeader=PResFunUploadDataHeaderFirst(pbyte(ResParams)+sizeof(TResFunUploadParams)); + if (Full) + Source=pbyte(ResDataHeader)+4; // skip only the mini header + else + Source=pbyte(ResDataHeader)+sizeof(TResFunUploadDataHeaderFirst); // not full : skip the data header + // Init Header + PDUH_out->P=0x32; // Always 0x32 + PDUH_out->PDUType=PduType_request; // 0x01 + PDUH_out->AB_EX=0x0000; // Always 0x0000 + PDUH_out->Sequence=GetNextWord(); // AutoInc + PDUH_out->ParLen=SwapWord(sizeof(TReqFunUploadParams));// params size + PDUH_out->DataLen=0x0000; // No data + // Init Params + ReqParams->FunUpld=pduUpload; + ReqParams->Uk6[0]=0x00; + ReqParams->Uk6[1]=0x00; + ReqParams->Uk6[2]=0x00; + ReqParams->Uk6[3]=0x00; + ReqParams->Uk6[4]=0x00; + ReqParams->Uk6[5]=0x00; + ReqParams->Upload_ID=Upload_ID; // At begining is 0) we will put upload id incoming from plc + + IsoSize=sizeof(TS7ReqHeader)+sizeof(TReqFunUploadParams); + Result=isoExchangeBuffer(0,IsoSize); + // Get Upload Infos (only ID now) + if (Result==0) + { + if (Answer->Error==0) + { + Done=ResParams->EoU==0; + if (Full) + Size=SwapWord(Answer->DataLen)-4; // Full data Size + else + Size=SwapWord(Answer->DataLen)-sizeof(TResFunUploadDataHeaderFirst); // Size of this data slice + + BlockLength=SwapWord(ResDataHeader->MC7Len); // Full block size in byte + Target=pbyte(&opData)+Offset; + memcpy(Target, Source, Size); + Offset+=Size; + } + else + Result=errCliUploadSequenceFailed; + }; + //-------------------------------------------------------->FirstUpload + while (!Done && (Result==0)) + { + //<----------------------------------------------------NextUpload + PReqFunUploadParams ReqParams; + PResFunUploadParams ResParams; + PResFunUploadDataHeaderNext ResDataHeader; + pbyte Source; + pbyte Target; + int Size; + + // Setup pointers (note : PDUH_out and PDU.Payload are the same pointer) + ReqParams=PReqFunUploadParams(pbyte(PDUH_out)+sizeof(TS7ReqHeader)); + // Next upload pdu consists of params, small info header, data. + ResParams=PResFunUploadParams(pbyte(Answer)+ResHeaderSize23); + ResDataHeader=PResFunUploadDataHeaderNext(pbyte(ResParams)+sizeof(TResFunUploadParams)); + Source=pbyte(ResDataHeader)+sizeof(TResFunUploadDataHeaderNext); + // Init Header + PDUH_out->P=0x32; // Always 0x32 + PDUH_out->PDUType=PduType_request; // 0x01 + PDUH_out->AB_EX=0x0000; // Always 0x0000 + PDUH_out->Sequence=GetNextWord(); // AutoInc + PDUH_out->ParLen=SwapWord(sizeof(TReqFunUploadParams));// params size + PDUH_out->DataLen=0x0000; // No data + // Init Params + ReqParams->FunUpld=pduUpload; + ReqParams->Uk6[0]=0x00; + ReqParams->Uk6[1]=0x00; + ReqParams->Uk6[2]=0x00; + ReqParams->Uk6[3]=0x00; + ReqParams->Uk6[4]=0x00; + ReqParams->Uk6[5]=0x00; + ReqParams->Upload_ID=Upload_ID; // At begining is 0) we will put upload id incoming from plc + + IsoSize=sizeof(TS7ReqHeader)+sizeof(TReqFunUploadParams); + Result=isoExchangeBuffer(0,IsoSize); + // Get Upload Infos (only ID now) + if (Result==0) + { + if (Answer->Error==0) + { + Done=ResParams->EoU==0; + Size=SwapWord(Answer->DataLen)-sizeof(TResFunUploadDataHeaderNext); // Size of this data slice + Target=pbyte(&opData)+Offset; + memcpy(Target, Source, Size); + Offset+=Size; + } + else + Result=errCliUploadSequenceFailed; + }; + //---------------------------------------------------->NextUpload + } + if (Result==0) + { + //<----------------------------------------------------EndUpload; + PReqFunEndUploadParams ReqParams; + PResFunEndUploadParams ResParams; + + // Setup pointers (note : PDUH_out and PDU.Payload are the same pointer) + ReqParams=PReqFunEndUploadParams(pbyte(PDUH_out)+sizeof(TS7ReqHeader)); + ResParams=PResFunEndUploadParams(pbyte(Answer)+ResHeaderSize23); + // Init Header + PDUH_out->P=0x32; // Always 0x32 + PDUH_out->PDUType=PduType_request; // 0x01 + PDUH_out->AB_EX=0x0000; // Always 0x0000 + PDUH_out->Sequence=GetNextWord(); // AutoInc + PDUH_out->ParLen=SwapWord(sizeof(TReqFunEndUploadParams));// params size + PDUH_out->DataLen=0x0000; // No data + // Init Params + ReqParams->FunEUpld=pduEndUpload; + ReqParams->Uk6[0]=0x00; + ReqParams->Uk6[1]=0x00; + ReqParams->Uk6[2]=0x00; + ReqParams->Uk6[3]=0x00; + ReqParams->Uk6[4]=0x00; + ReqParams->Uk6[5]=0x00; + ReqParams->Upload_ID=Upload_ID; // At begining is 0) we will put upload id incoming from plc + + IsoSize=sizeof(TS7ReqHeader)+sizeof(TReqFunEndUploadParams); + Result=isoExchangeBuffer(0,IsoSize); + + // Get EndUpload Result + if (Result==0) + { + if ((Answer->Error!=0) || (ResParams->FunEUpld!=pduEndUpload)) + Result=errCliUploadSequenceFailed; + }; + //---------------------------------------------------->EndUpload; + } + }; + + *Job.pAmount=0; + if (Result==0) + { + if (Full) + { + opSize=int(Offset); + if (opSize<78) + Result=errCliInvalidDataSizeRecvd; + } + else + { + opSize=BlockLength; + if (opSize<1) + Result=errCliInvalidDataSizeRecvd; + }; + if (Result==0) + { + // Checks user space + if (Job.Amount override if romerror + Result=errCliPartialDataRead; + }; + }; + return Result; +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::opDownload() +{ + PS7CompactBlockInfo Info; + PS7BlockFooter Footer; + int BlockNum, StoreBlockNum, BlockAmount; + int BlockSize, BlockSizeLd; + int BlockType, Remainder; + int Result, IsoSize; + bool Done = false; + uintptr_t Offset; + + BlockAmount=Job.Amount; + BlockNum =Job.Number; + Result=CheckBlock(-1,-1,&opData,BlockAmount); + if (Result==0) + { + Info=PS7CompactBlockInfo(&opData); + // Gets blocktype + BlockType=SubBlockToBlock(Info->SubBlkType); + + if (BlockNum>=0) + Info->BlkNum=SwapWord(BlockNum); // change the number + else + BlockNum=SwapWord(Info->BlkNum); // use the header's number + + BlockSizeLd=BlockAmount; // load mem needed for this block + BlockSize =SwapWord(Info->MC7Len); // net size + Footer=PS7BlockFooter(pbyte(&opData)+BlockSizeLd-sizeof(TS7BlockFooter)); + Footer->Chksum=0x0000; + + Offset=0; + Remainder=BlockAmount; + //<---------------------------------------------- Start Download request + PReqStartDownloadParams ReqParams; + PResStartDownloadParams ResParams; + PS7ResHeader23 Answer; + + // Setup pointers (note : PDUH_out and PDU.Payload are the same pointer) + ReqParams=PReqStartDownloadParams(pbyte(PDUH_out)+sizeof(TS7ReqHeader)); + Answer =PS7ResHeader23(&PDU.Payload); + ResParams=PResStartDownloadParams(pbyte(Answer)+ResHeaderSize23); + // Init Header + PDUH_out->P=0x32; // Always 0x32 + PDUH_out->PDUType=PduType_request; // 0x01 + PDUH_out->AB_EX=0x0000; // Always 0x0000 + PDUH_out->Sequence=GetNextWord(); // AutoInc + PDUH_out->ParLen=SwapWord(sizeof(TReqStartDownloadParams)); + PDUH_out->DataLen=0x0000; // No data + // Init Params + ReqParams->FunSDwnld = pduReqDownload; + ReqParams->Uk6[0]=0x00; + ReqParams->Uk6[1]=0x01; + ReqParams->Uk6[2]=0x00; + ReqParams->Uk6[3]=0x00; + ReqParams->Uk6[4]=0x00; + ReqParams->Uk6[5]=0x00; + ReqParams->Dwnld_ID=0x00; + ReqParams->Len_1 =0x09; + ReqParams->Prefix=0x5F; + ReqParams->BlkPrfx=0x30; + ReqParams->BlkType=BlockType; + StoreBlockNum=BlockNum; + ReqParams->AsciiBlk[0]=(BlockNum / 10000)+0x30; + BlockNum=BlockNum % 10000; + ReqParams->AsciiBlk[1]=(BlockNum / 1000)+0x30; + BlockNum=BlockNum % 1000; + ReqParams->AsciiBlk[2]=(BlockNum / 100)+0x30; + BlockNum=BlockNum % 100; + ReqParams->AsciiBlk[3]=(BlockNum / 10)+0x30; + BlockNum=BlockNum % 10; + ReqParams->AsciiBlk[4]=(BlockNum / 1)+0x30; + ReqParams->P =0x50; + ReqParams->Len_2=0x0D; + ReqParams->Uk1 =0x31; // '1' + BlockNum=StoreBlockNum; + // Load memory + ReqParams->AsciiLoad[0]=(BlockSizeLd / 100000)+0x30; + BlockSizeLd=BlockSizeLd % 100000; + ReqParams->AsciiLoad[1]=(BlockSizeLd / 10000)+0x30; + BlockSizeLd=BlockSizeLd % 10000; + ReqParams->AsciiLoad[2]=(BlockSizeLd / 1000)+0x30; + BlockSizeLd=BlockSizeLd % 1000; + ReqParams->AsciiLoad[3]=(BlockSizeLd / 100)+0x30; + BlockSizeLd=BlockSizeLd % 100; + ReqParams->AsciiLoad[4]=(BlockSizeLd / 10)+0x30; + BlockSizeLd=BlockSizeLd % 10; + ReqParams->AsciiLoad[5]=(BlockSizeLd / 1)+0x30; + // MC7 memory + ReqParams->AsciiMC7[0]=(BlockSize / 100000)+0x30; + BlockSize=BlockSize % 100000; + ReqParams->AsciiMC7[1]=(BlockSize / 10000)+0x30; + BlockSize=BlockSize % 10000; + ReqParams->AsciiMC7[2]=(BlockSize / 1000)+0x30; + BlockSize=BlockSize % 1000; + ReqParams->AsciiMC7[3]=(BlockSize / 100)+0x30; + BlockSize=BlockSize % 100; + ReqParams->AsciiMC7[4]=(BlockSize / 10)+0x30; + BlockSize=BlockSize % 10; + ReqParams->AsciiMC7[5]=(BlockSize / 1)+0x30; + + IsoSize=sizeof(TS7ReqHeader)+sizeof(TReqStartDownloadParams); + Result=isoExchangeBuffer(0,IsoSize); + + // Get Result + if (Result==0) + { + if (SwapWord(Answer->Error)!=Code7NeedPassword) + { + if ((Answer->Error!=0) || (*ResParams!=pduReqDownload)) + Result=errCliDownloadSequenceFailed; + } + else + Result=errCliNeedPassword; + } + //----------------------------------------------> Start Download request + if (Result==0) + { + do + { + //<-------------------------------- Download sequence (PLC requests) + PReqDownloadParams ReqParams; + PS7ResHeader23 Answer; + PResDownloadParams ResParams; + PResDownloadDataHeader ResData; + int Slice, Size, MaxSlice; + word Sequence; + pbyte Source; + pbyte Target; + + ReqParams=PReqDownloadParams(pbyte(PDUH_out)+sizeof(TS7ReqHeader)); + Answer =PS7ResHeader23(&PDU.Payload); + ResParams=PResDownloadParams(pbyte(Answer)+ResHeaderSize23); + ResData =PResDownloadDataHeader(pbyte(ResParams)+sizeof(TResDownloadParams)); + Target =pbyte(ResData)+sizeof(TResDownloadDataHeader); + Source =pbyte(&opData)+Offset; + + Result=isoRecvBuffer(0,Size); + if (Result==0) + { + if ((u_int(Size)>sizeof(TS7ReqHeader)) && (ReqParams->Fun==pduDownload)) + { + Sequence=PDUH_out->Sequence; + // Max data slice that we can fit in this pdu + MaxSlice=PDULength-ResHeaderSize23-sizeof(TResDownloadParams)-sizeof(TResDownloadDataHeader); + Slice=Remainder; + if (Slice>MaxSlice) + Slice=MaxSlice; + Remainder-=Slice; + Offset+=Slice; + Done=Remainder<=0; + // Init Answer + Answer->P=0x32; + Answer->PDUType=PduType_response; + Answer->AB_EX=0x0000; + Answer->Sequence=Sequence; + Answer->ParLen =SwapWord(sizeof(TResDownloadParams)); + Answer->DataLen=SwapWord(word(sizeof(TResDownloadDataHeader))+Slice); + Answer->Error =0x0000; + + // Init Params + ResParams->FunDwnld=pduDownload; + if (Remainder>0) + ResParams->EoS=0x01; + else + ResParams->EoS=0x00; + + // Init Data + ResData->DataLen=SwapWord(Slice); + ResData->FB_00=0xFB00; + memcpy(Target, Source, Slice); + + // Send the slice + IsoSize=ResHeaderSize23+sizeof(TResDownloadParams)+sizeof(TResDownloadDataHeader)+Slice; + Result=isoSendBuffer(0,IsoSize); + } + else + Result=errCliDownloadSequenceFailed; + }; + //--------------------------------> Download sequence (PLC requests) + } + while (!Done && (Result==0)); + + if (Result==0) + { + //<-------------------------------------------Perform Download Ended + PReqDownloadParams ReqParams; + PS7ResHeader23 Answer; + PResEndDownloadParams ResParams; + int Size; + word Sequence; + + ReqParams=PReqDownloadParams(pbyte(PDUH_out)+sizeof(TS7ReqHeader)); + Answer =PS7ResHeader23(&PDU.Payload); + ResParams=PResEndDownloadParams(pbyte(Answer)+ResHeaderSize23); + + Result=isoRecvBuffer(0,Size); + if (Result==0) + { + if ((u_int(Size)>sizeof(TS7ReqHeader)) && (ReqParams->Fun==pduDownloadEnded)) + { + Sequence=PDUH_out->Sequence; + // Init Answer + Answer->P=0x32; + Answer->PDUType=PduType_response; + Answer->AB_EX=0x0000; + Answer->Sequence=Sequence; + Answer->ParLen =SwapWord(sizeof(TResEndDownloadParams)); + Answer->DataLen=0x0000; + Answer->Error =0x0000; + + // Init Params + *ResParams=pduDownloadEnded; + IsoSize=ResHeaderSize23+sizeof(TResEndDownloadParams); + Result=isoSendBuffer(0,IsoSize); + } + else + Result=errCliDownloadSequenceFailed; + }; + //------------------------------------------->Perform Download Ended + if (Result==0) + { + //<----------------------------------- Insert block into the unit + PReqControlBlockParams ReqParams; + PS7ResHeader23 Answer; + pbyte ResParams; + + // Setup pointers (note : PDUH_out and PDU.Payload are the same pointer) + ReqParams=PReqControlBlockParams(pbyte(PDUH_out)+sizeof(TS7ReqHeader)); + Answer=PS7ResHeader23(&PDU.Payload); + ResParams=pbyte(Answer)+ResHeaderSize23; + // Init Header + PDUH_out->P=0x32; // Always 0x32 + PDUH_out->PDUType=PduType_request; // 0x01 + PDUH_out->AB_EX=0x0000; // Always 0x0000 + PDUH_out->Sequence=GetNextWord(); // AutoInc + PDUH_out->ParLen=SwapWord(sizeof(TReqControlBlockParams)); + PDUH_out->DataLen=0x0000; // No data + // Init Params + ReqParams->Fun = pduControl; + ReqParams->Uk7[0]=0x00; + ReqParams->Uk7[1]=0x00; + ReqParams->Uk7[2]=0x00; + ReqParams->Uk7[3]=0x00; + ReqParams->Uk7[4]=0x00; + ReqParams->Uk7[5]=0x00; + ReqParams->Uk7[6]=0xFD; + ReqParams->Len_1 =SwapWord(0x0A); + ReqParams->NumOfBlocks=0x01; + ReqParams->ByteZero =0x00; + ReqParams->AsciiZero =0x30; + ReqParams->BlkType=BlockType; + ReqParams->AsciiBlk[0]=(BlockNum / 10000)+0x30; + BlockNum=BlockNum % 10000; + ReqParams->AsciiBlk[1]=(BlockNum / 1000)+0x30; + BlockNum=BlockNum % 1000; + ReqParams->AsciiBlk[2]=(BlockNum / 100)+0x30; + BlockNum=BlockNum % 100; + ReqParams->AsciiBlk[3]=(BlockNum / 10)+0x30; + BlockNum=BlockNum % 10; + ReqParams->AsciiBlk[4]=(BlockNum / 1)+0x30; + ReqParams->SFun =SFun_Insert; + ReqParams->Len_2=0x05; + ReqParams->Cmd[0]='_'; + ReqParams->Cmd[1]='I'; + ReqParams->Cmd[2]='N'; + ReqParams->Cmd[3]='S'; + ReqParams->Cmd[4]='E'; + + IsoSize=sizeof(TS7ReqHeader)+sizeof(TReqControlBlockParams); + Result=isoExchangeBuffer(0,IsoSize); + + if (Result==0) + { + if ((Answer->Error!=0) || (*ResParams!=pduControl)) + Result=errCliInsertRefused; + }; + //-----------------------------------> Insert block into the unit + } + }; + }; + }; + return Result; +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::opDelete() +{ + PReqControlBlockParams ReqParams; + PS7ResHeader23 Answer; + pbyte ResParams; + int IsoSize, BlockType, BlockNum, Result; + + BlockType=Job.Area; + BlockNum =Job.Number; + // Setup pointers (note : PDUH_out and PDU.Payload are the same pointer) + ReqParams=PReqControlBlockParams(pbyte(PDUH_out)+sizeof(TS7ReqHeader)); + Answer =PS7ResHeader23(&PDU.Payload); + ResParams=pbyte(Answer)+ResHeaderSize23; + // Init Header + PDUH_out->P=0x32; // Always 0x32 + PDUH_out->PDUType=PduType_request; // 0x01 + PDUH_out->AB_EX=0x0000; // Always 0x0000 + PDUH_out->Sequence=GetNextWord(); // AutoInc + PDUH_out->ParLen=SwapWord(sizeof(TReqControlBlockParams)); + PDUH_out->DataLen=0x0000; // No data + // Init Params + ReqParams->Fun = pduControl; + ReqParams->Uk7[0]=0x00; + ReqParams->Uk7[1]=0x00; + ReqParams->Uk7[2]=0x00; + ReqParams->Uk7[3]=0x00; + ReqParams->Uk7[4]=0x00; + ReqParams->Uk7[5]=0x00; + ReqParams->Uk7[6]=0xFD; + ReqParams->Len_1 =SwapWord(0x0A); + ReqParams->NumOfBlocks=0x01; + ReqParams->ByteZero =0x00; + ReqParams->AsciiZero =0x30; + ReqParams->BlkType=BlockType; + ReqParams->AsciiBlk[0]=(BlockNum / 10000)+0x30; + BlockNum=BlockNum % 10000; + ReqParams->AsciiBlk[1]=(BlockNum / 1000)+0x30; + BlockNum=BlockNum % 1000; + ReqParams->AsciiBlk[2]=(BlockNum / 100)+0x30; + BlockNum=BlockNum % 100; + ReqParams->AsciiBlk[3]=(BlockNum / 10)+0x30; + BlockNum=BlockNum % 10; + ReqParams->AsciiBlk[4]=(BlockNum / 1)+0x30; + ReqParams->SFun =SFun_Delete; + ReqParams->Len_2=0x05; + ReqParams->Cmd[0]='_'; + ReqParams->Cmd[1]='D'; + ReqParams->Cmd[2]='E'; + ReqParams->Cmd[3]='L'; + ReqParams->Cmd[4]='E'; + + IsoSize=sizeof(TS7ReqHeader)+sizeof(TReqControlBlockParams); + Result=isoExchangeBuffer(0,IsoSize); + + if (Result==0) + { + if (SwapWord(Answer->Error)!=Code7NeedPassword) + { + if ((Answer->Error!=0) || (*ResParams!=pduControl)) + Result=errCliDeleteRefused; + } + else + Result=errCliNeedPassword; + } + return Result; +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::opReadSZL() +{ + PS7Answer17 Answer; + PReqFunReadSZLFirst ReqParamsFirst; + PReqFunReadSZLNext ReqParamsNext; + PS7ReqSZLData ReqDataFirst; + PS7ReqSZLData ReqDataNext; + PS7ResParams7 ResParams; + PS7ResSZLDataFirst ResDataFirst; + PS7ResSZLDataNext ResDataNext; + PSZL_HEADER Header; + PS7SZLList Target; + pbyte PDataFirst; + pbyte PDataNext; + word ID, Index; + int IsoSize, DataSize, DataSZL, Result; + bool First, Done; + bool NoRoom = false; + uintptr_t Offset =0; + byte Seq_in =0x00; + + ID=Job.ID; + Index=Job.Index; + opSize=0; + // Setup pointers (note : PDUH_out and PDU.Payload are the same pointer) + ReqParamsFirst=PReqFunReadSZLFirst(pbyte(PDUH_out)+sizeof(TS7ReqHeader)); + ReqParamsNext =PReqFunReadSZLNext(pbyte(PDUH_out)+sizeof(TS7ReqHeader)); + ReqDataFirst =PS7ReqSZLData(pbyte(ReqParamsFirst)+sizeof(TReqFunReadSZLFirst)); + ReqDataNext =PS7ReqSZLData(pbyte(ReqParamsNext)+sizeof(TReqFunReadSZLNext)); + + Answer =PS7Answer17(&PDU.Payload); + ResParams =PS7ResParams7(pbyte(Answer)+ResHeaderSize17); + ResDataFirst =PS7ResSZLDataFirst(pbyte(ResParams)+sizeof(TS7Params7)); + ResDataNext =PS7ResSZLDataNext(pbyte(ResParams)+sizeof(TS7Params7)); + PDataFirst =pbyte(ResDataFirst)+8; // skip header + PDataNext =pbyte(ResDataNext)+4; // skip header + Header =PSZL_HEADER(&opData); + First=true; + Done =false; + do + { + //<------------------------------------------------------- read slices + if (First) + { + //<-------------------------------------------------- prepare first + DataSize=sizeof(TS7ReqSZLData); + // Fill Header + PDUH_out->P=0x32; // Always 0x32 + PDUH_out->PDUType=PduType_userdata; // 0x07 + PDUH_out->AB_EX=0x0000; // Always 0x0000 + PDUH_out->Sequence=GetNextWord(); // AutoInc + PDUH_out->ParLen=SwapWord(sizeof(TReqFunReadSZLFirst)); // 8 bytes params + PDUH_out->DataLen=SwapWord(DataSize); // 8/4 bytes data + // Fill Params + ReqParamsFirst->Head[0]=0x00; + ReqParamsFirst->Head[1]=0x01; + ReqParamsFirst->Head[2]=0x12; + ReqParamsFirst->Plen =0x04; + ReqParamsFirst->Uk =0x11; + ReqParamsFirst->Tg =grSZL; + ReqParamsFirst->SubFun =SFun_ReadSZL; //0x03 + ReqParamsFirst->Seq =Seq_in; + // Fill Data + ReqDataFirst->Ret =0xFF; + ReqDataFirst->TS =TS_ResOctet; + ReqDataFirst->DLen =SwapWord(0x0004); + ReqDataFirst->ID =SwapWord(ID); + ReqDataFirst->Index =SwapWord(Index); + IsoSize=sizeof(TS7ReqHeader)+sizeof(TReqFunReadSZLFirst)+DataSize; + //--------------------------------------------------> prepare first + } + else + { + //<-------------------------------------------------- prepare next + DataSize=sizeof(TS7ReqSZLData)-4; + // Fill Header + PDUH_out->P=0x32; // Always 0x32 + PDUH_out->PDUType=PduType_userdata; // 0x07 + PDUH_out->AB_EX=0x0000; // Always 0x0000 + PDUH_out->Sequence=GetNextWord(); // AutoInc + PDUH_out->ParLen=SwapWord(sizeof(TReqFunReadSZLNext)); // 8 bytes params + PDUH_out->DataLen=SwapWord(DataSize);// 8/4 bytes data + // Fill Params + ReqParamsNext->Head[0]=0x00; + ReqParamsNext->Head[1]=0x01; + ReqParamsNext->Head[2]=0x12; + ReqParamsNext->Plen =0x08; + ReqParamsNext->Uk =0x12; + ReqParamsNext->Tg =grSZL; + ReqParamsNext->SubFun =SFun_ReadSZL; + ReqParamsNext->Seq =Seq_in; + ReqParamsNext->Rsvd =0x0000; + ReqParamsNext->ErrNo =0x0000; + // Fill Data + ReqDataNext->Ret =0x0A; + ReqDataNext->TS =0x00; + ReqDataNext->DLen =0x0000; + ReqDataNext->ID =0x0000; + ReqDataNext->Index =0x0000; + IsoSize=sizeof(TS7ReqHeader)+sizeof(TReqFunReadSZLNext)+DataSize; + //--------------------------------------------------> prepare next + } + Result=isoExchangeBuffer(0,IsoSize); + // Get Data + if (Result==0) + { + if (First) + { + //<------------------------------------------ get data first + if (ResParams->Err==0) + { + if (ResDataFirst->Ret==0xFF) // <-- 0xFF means Result OK + { + // Gets Amount of this slice + DataSZL=SwapWord(ResDataFirst->DLen)-4;// Skips extra params (ID, Index ...) + // Gets end of Sequence Flag + Done=(ResParams->resvd & 0xFF00) == 0; // Low order byte = 0x00 => the sequence is done + // Gets Unit's function sequence + Seq_in=ResParams->Seq; + Target=PS7SZLList(pbyte(&opData)+Offset); + memcpy(Target, PDataFirst, DataSZL); + Offset+=DataSZL; + } + else + Result=CpuError(ResDataFirst->Ret); + } + else + Result=CpuError(ResDataFirst->Ret); + //------------------------------------------> get data first + } + else + { + //<------------------------------------------ get data next + if (ResParams->Err==0) + { + if (ResDataNext->Ret==0xFF) // <-- 0xFF means Result OK + { + // Gets Amount of this slice + DataSZL=SwapWord(ResDataNext->DLen); + // Gets end of Sequence Flag + Done=(ResParams->resvd & 0xFF00) == 0; // Low order byte = 0x00 => the sequence is done + // Gets Unit's function sequence + Seq_in=ResParams->Seq; + Target=PS7SZLList(pbyte(&opData)+Offset); + memcpy(Target, PDataNext, DataSZL); + Offset+=DataSZL; + } + else + Result=CpuError(ResDataNext->Ret); + } + else + Result=CpuError(ResDataNext->Ret); + //------------------------------------------> get data next + } + First=false; + } + //-------------------------------------------------------> read slices + } + while ((!Done) && (Result==0)); + + // Check errors and adjust header + if (Result==0) + { + // Adjust big endian header + Header->LENTHDR=SwapWord(Header->LENTHDR); + Header->N_DR =SwapWord(Header->N_DR); + opSize=int(Offset); + + if (Job.IParam==1) // if 1 data has to be copied into user buffer + { + // Check buffer size + if (opSize>Job.Amount) + { + opSize=Job.Amount; + NoRoom=true; + } + memcpy(Job.pData, &opData, opSize); + *Job.pAmount=opSize; + }; + }; + if ((Result==0)&& NoRoom) + Result=errCliBufferTooSmall; + + return Result; +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::opReadSZLList() +{ + PS7SZLList usrSZLList, opDataList; + int ItemsCount, ItemsCount_in, c, Result; + bool NoRoom = false; + + Job.ID =0x0000; + Job.Index =0x0000; + Job.IParam =0; + ItemsCount_in=Job.Amount; // stores the room + Job.Amount =sizeof(opData); // read into the internal buffer + + Result =opReadSZL(); + if (Result==0) + { + opDataList=PS7SZLList(&opData); // Source + usrSZLList=PS7SZLList(Job.pData); // Target + + ItemsCount=(opSize-sizeof(SZL_HEADER)) / 2; + // Check input size + if (ItemsCount>ItemsCount_in) + { + ItemsCount=ItemsCount_in; // Trim itemscount + NoRoom=true; + } + for (c = 0; c < ItemsCount; c++) + usrSZLList->List[c]=SwapWord(opDataList->List[c]); + *Job.pAmount=ItemsCount; + } + else + *Job.pAmount=0; + + if ((Result==0) && NoRoom) + Result=errCliBufferTooSmall; + + return Result; +} +//--------------------------------------------------------------------------- +byte TSnap7MicroClient::BCDtoByte(byte B) +{ + return ((B >> 4) * 10) + (B & 0x0F); +} +//--------------------------------------------------------------------------- +byte TSnap7MicroClient::WordToBCD(word Value) +{ + return ((Value / 10) << 4) | (Value % 10); +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::opGetDateTime() +{ + PTimeStruct DateTime; + PReqFunDateTime ReqParams; + PReqDataGetDateTime ReqData; + PS7ResParams7 ResParams; + PResDataGetTime ResData; + PS7ResHeader17 Answer; + int IsoSize, Result; + word AYear; + + // Setup pointers (note : PDUH_out and PDU.Payload are the same pointer) + ReqParams=PReqFunDateTime(pbyte(PDUH_out)+sizeof(TS7ReqHeader)); + ReqData =PReqDataGetDateTime(pbyte(ReqParams)+sizeof(TReqFunDateTime)); + Answer =PS7ResHeader17(&PDU.Payload); + ResParams=PS7ResParams7(pbyte(Answer)+ResHeaderSize17); + ResData =PResDataGetTime(pbyte(ResParams)+sizeof(TS7Params7)); + DateTime =PTimeStruct(Job.pData); + // Fill Header + PDUH_out->P=0x32; // Always 0x32 + PDUH_out->PDUType=PduType_userdata; // 0x07 + PDUH_out->AB_EX=0x0000; // Always 0x0000 + PDUH_out->Sequence=GetNextWord(); // AutoInc + PDUH_out->ParLen=SwapWord(sizeof(TReqFunDateTime)); // 8 bytes params + PDUH_out->DataLen=SwapWord(sizeof(TReqDataGetDateTime)); // 4 bytes data + // Fill params (mostly constants) + ReqParams->Head[0]=0x00; + ReqParams->Head[1]=0x01; + ReqParams->Head[2]=0x12; + ReqParams->Plen =0x04; + ReqParams->Uk =0x11; + ReqParams->Tg =grClock; + ReqParams->SubFun =SFun_ReadClock; + ReqParams->Seq =0x00; + // Fill Data + *ReqData =0x0000000A; + + IsoSize=sizeof(TS7ReqHeader)+sizeof(TReqFunDateTime)+sizeof(TReqDataGetDateTime); + Result=isoExchangeBuffer(0,IsoSize); + + // Get Data + if (Result==0) + { + if (ResParams->Err==0) + { + if (ResData->RetVal==0xFF) // <-- 0xFF means Result OK + { + // Decode Plc Date and Time + AYear=BCDtoByte(ResData->Time[0]); + if (AYear<90) + AYear=AYear+100; + DateTime->tm_year=AYear; + DateTime->tm_mon =BCDtoByte(ResData->Time[1])-1; + DateTime->tm_mday=BCDtoByte(ResData->Time[2]); + DateTime->tm_hour=BCDtoByte(ResData->Time[3]); + DateTime->tm_min =BCDtoByte(ResData->Time[4]); + DateTime->tm_sec =BCDtoByte(ResData->Time[5]); + DateTime->tm_wday=(ResData->Time[7] & 0x0F)-1; + } + else + Result=CpuError(ResData->RetVal); + } + else + Result=CpuError(ResData->RetVal); + } + return Result; +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::opSetDateTime() +{ + PTimeStruct DateTime; + PReqFunDateTime ReqParams; + PReqDataSetTime ReqData; + PS7ResParams7 ResParams; + PS7ResHeader17 Answer; + word AYear; + int IsoSize, Result; + + // Setup pointers (note : PDUH_out and PDU.Payload are the same pointer) + ReqParams=PReqFunDateTime(pbyte(PDUH_out)+sizeof(TS7ReqHeader)); + ReqData =PReqDataSetTime(pbyte(ReqParams)+sizeof(TReqFunDateTime)); + Answer =PS7ResHeader17(&PDU.Payload); + ResParams=PS7ResParams7(pbyte(Answer)+ResHeaderSize17); + DateTime =PTimeStruct(Job.pData); + // Fill Header + PDUH_out->P=0x32; // Always 0x32 + PDUH_out->PDUType=PduType_userdata; // 0x07 + PDUH_out->AB_EX=0x0000; // Always 0x0000 + PDUH_out->Sequence=GetNextWord(); // AutoInc + PDUH_out->ParLen=SwapWord(sizeof(TReqFunDateTime)); // 8 bytes params + PDUH_out->DataLen=SwapWord(sizeof(TReqDataSetTime)); // 4 bytes data + // Fill params (mostly constants) + ReqParams->Head[0]=0x00; + ReqParams->Head[1]=0x01; + ReqParams->Head[2]=0x12; + ReqParams->Plen =0x04; + ReqParams->Uk =0x11; + ReqParams->Tg =grClock; + ReqParams->SubFun =SFun_SetClock; + ReqParams->Seq =0x00; + // EncodeSiemensDateTime; + if (DateTime->tm_year<100) + AYear=DateTime->tm_year; + else + AYear=DateTime->tm_year-100; + + ReqData->RetVal=0xFF; + ReqData->TSize =TS_ResOctet; + ReqData->Length=SwapWord(0x000A); + ReqData->Rsvd =0x00; + ReqData->HiYear=0x19; // *must* be 19 tough it's not the Hi part of the year... + + ReqData->Time[0]=WordToBCD(AYear); + ReqData->Time[1]=WordToBCD(DateTime->tm_mon+1); + ReqData->Time[2]=WordToBCD(DateTime->tm_mday); + ReqData->Time[3]=WordToBCD(DateTime->tm_hour); + ReqData->Time[4]=WordToBCD(DateTime->tm_min); + ReqData->Time[5]=WordToBCD(DateTime->tm_sec); + ReqData->Time[6]=0; + ReqData->Time[7]=DateTime->tm_wday+1; + + IsoSize=sizeof(TS7ReqHeader)+sizeof(TReqFunDateTime)+sizeof(TReqDataSetTime); + Result=isoExchangeBuffer(0,IsoSize); + + // Get Result + if (Result==0) + { + if (ResParams->Err!=0) + Result=CpuError(SwapWord(ResParams->Err)); + }; + return Result; +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::opGetOrderCode() +{ + PS7OrderCode OC; + int Result; + + Job.ID =0x0011; + Job.Index =0x0000; + Job.IParam =0; + Result =opReadSZL(); + if (Result==0) + { + OC=PS7OrderCode(Job.pData); + memset(OC,0,sizeof(TS7OrderCode)); + memcpy(OC->Code,&opData[6],20); + OC->V1=opData[opSize-3]; + OC->V2=opData[opSize-2]; + OC->V3=opData[opSize-1]; + }; + return Result; +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::opGetCpuInfo() +{ + PS7CpuInfo Info; + int Result; + + // Store Pointer + Info=PS7CpuInfo(Job.pData); + // Clear data in order to have the end of strings (\0) correctly setted + memset(Info, 0, sizeof(TS7CpuInfo)); + + Job.ID =0x001C; + Job.Index =0x0000; + Job.IParam=0; + Result =opReadSZL(); + if (Result==0) + { + memcpy(Info->ModuleTypeName,&opData[176],32); + memcpy(Info->SerialNumber,&opData[142],24); + memcpy(Info->ASName,&opData[6],24); + memcpy(Info->Copyright,&opData[108],26); + memcpy(Info->ModuleName,&opData[40],24); + } + return Result; +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::opGetCpInfo() +{ + PS7CpInfo Info; + int Result; + // Store Pointer + Info=PS7CpInfo(Job.pData); + memset(Info,0,sizeof(TS7CpInfo)); + Job.ID =0x0131; + Job.Index =0x0001; + Job.IParam=0; + Result =opReadSZL(); + if (Result==0) + { + Info->MaxPduLengt=opData[6]*256+opData[7]; + Info->MaxConnections=opData[8]*256+opData[9]; + Info->MaxMpiRate=DWordAt(&opData[10]); + Info->MaxBusRate=DWordAt(&opData[14]); + }; + return Result; +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::opGetPlcStatus() +{ + int *Status; + int Result; + + Status =(int*)Job.pData; + Job.ID =0x0424; + Job.Index =0x0000; + Job.IParam =0; + Result =opReadSZL(); + if (Result==0) + { + switch (opData[7]) + { + case S7CpuStatusUnknown : + case S7CpuStatusRun : + case S7CpuStatusStop : *Status=opData[7]; + break; + default : + // Since RUN status is always $08 for all CPUs and CPs, STOP status + // sometime can be coded as $03 (especially for old cpu...) + *Status=S7CpuStatusStop; + } + } + else + *Status=0; + return Result; +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::opPlcStop() +{ + PReqFunPlcStop ReqParams; + PResFunCtrl ResParams; + PS7ResHeader23 Answer; + int IsoSize, Result; + + char p_program[] = {'P','_','P','R','O','G','R','A','M'}; + + // Setup pointers (note : PDUH_out and PDU.Payload are the same pointer) + ReqParams=PReqFunPlcStop(pbyte(PDUH_out)+sizeof(TS7ReqHeader)); + Answer =PS7ResHeader23(&PDU.Payload); + ResParams=PResFunCtrl(pbyte(Answer)+ResHeaderSize23); + // Fill Header + PDUH_out->P=0x32; // Always 0x32 + PDUH_out->PDUType=PduType_request; // 0x01 + PDUH_out->AB_EX=0x0000; // Always 0x0000 + PDUH_out->Sequence=GetNextWord(); // AutoInc + PDUH_out->ParLen=SwapWord(sizeof(TReqFunPlcStop)); + PDUH_out->DataLen=0x0000; // No Data + // Fill Params + ReqParams->Fun=pduStop; + memset(ReqParams->Uk_5,0,5); + ReqParams->Len_2=0x09; + memcpy(ReqParams->Cmd,&p_program,9); + + IsoSize=sizeof(TS7ReqHeader)+sizeof(TReqFunPlcStop); + Result=isoExchangeBuffer(0,IsoSize); + + if (Result==0) + { + if (Answer->Error!=0) + { + if (ResParams->ResFun!=pduStop) + Result=errCliCannotStopPLC; + else + if (ResParams->para ==0x07) + Result=errCliAlreadyStop; + else + Result=errCliCannotStopPLC; + }; + }; + return Result; +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::opPlcHotStart() +{ + PReqFunPlcHotStart ReqParams; + PResFunCtrl ResParams; + PS7ResHeader23 Answer; + int IsoSize, Result; + + char p_program[] = {'P','_','P','R','O','G','R','A','M'}; + + // Setup pointers (note : PDUH_out and PDU.Payload are the same pointer) + ReqParams=PReqFunPlcHotStart(pbyte(PDUH_out)+sizeof(TS7ReqHeader)); + Answer =PS7ResHeader23(&PDU.Payload); + ResParams=PResFunCtrl(pbyte(Answer)+ResHeaderSize23); + // Fill Header + PDUH_out->P=0x32; // Always 0x32 + PDUH_out->PDUType=PduType_request; // 0x01 + PDUH_out->AB_EX=0x0000; // Always 0x0000 + PDUH_out->Sequence=GetNextWord(); // AutoInc + PDUH_out->ParLen=SwapWord(sizeof(TReqFunPlcHotStart)); // 16 bytes params + PDUH_out->DataLen=0x0000; // No Data + // Fill Params + ReqParams->Fun=pduStart; + ReqParams->Uk_7[0]=0x00; + ReqParams->Uk_7[1]=0x00; + ReqParams->Uk_7[2]=0x00; + ReqParams->Uk_7[3]=0x00; + ReqParams->Uk_7[4]=0x00; + ReqParams->Uk_7[5]=0x00; + ReqParams->Uk_7[6]=0xFD; + + ReqParams->Len_1=0x0000; + ReqParams->Len_2=0x09; + memcpy(ReqParams->Cmd,&p_program,9); + + IsoSize=sizeof(TS7ReqHeader)+sizeof(TReqFunPlcHotStart); + Result=isoExchangeBuffer(0,IsoSize); + + if (Result==0) + { + if ((Answer->Error!=0)) + { + if ((ResParams->ResFun!=pduStart)) + Result=errCliCannotStartPLC; + else + { + if (ResParams->para==0x03) + Result=errCliAlreadyRun; + else + if (ResParams->para==0x02) + Result=errCliCannotStartPLC; + else + Result=errCliCannotStartPLC; + } + } + }; + return Result; +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::opPlcColdStart() +{ + PReqFunPlcColdStart ReqParams; + PResFunCtrl ResParams; + PS7ResHeader23 Answer; + int IsoSize, Result; + char p_program[] = {'P','_','P','R','O','G','R','A','M'}; + + // Setup pointers (note : PDUH_out and PDU.Payload are the same pointer) + ReqParams=PReqFunPlcColdStart(pbyte(PDUH_out)+sizeof(TS7ReqHeader)); + Answer =PS7ResHeader23(&PDU.Payload); + ResParams=PResFunCtrl(pbyte(Answer)+ResHeaderSize23); + // Fill Header + PDUH_out->P=0x32; // Always 0x32 + PDUH_out->PDUType=PduType_request; // 0x01 + PDUH_out->AB_EX=0x0000; // Always 0x0000 + PDUH_out->Sequence=GetNextWord(); // AutoInc + PDUH_out->ParLen=SwapWord(sizeof(TReqFunPlcColdStart)); // 22 bytes params + PDUH_out->DataLen=0x0000; // No Data + // Fill Params + ReqParams->Fun=pduStart; + ReqParams->Uk_7[0]=0x00; + ReqParams->Uk_7[1]=0x00; + ReqParams->Uk_7[2]=0x00; + ReqParams->Uk_7[3]=0x00; + ReqParams->Uk_7[4]=0x00; + ReqParams->Uk_7[5]=0x00; + ReqParams->Uk_7[6]=0xFD; + + ReqParams->Len_1=SwapWord(0x0002); + ReqParams->SFun =SwapWord(0x4320); // Cold start + ReqParams->Len_2=0x09; + memcpy(ReqParams->Cmd,&p_program,9); + + IsoSize=sizeof(TS7ReqHeader)+sizeof(TReqFunPlcColdStart); + Result=isoExchangeBuffer(0,IsoSize); + + if (Result==0) + { + if ((Answer->Error!=0)) + { + if ((ResParams->ResFun!=pduStart)) + Result=errCliCannotStartPLC; + else + { + if (ResParams->para==0x03) + Result=errCliAlreadyRun; + else + if (ResParams->para==0x02) + Result=errCliCannotStartPLC; + else + Result=errCliCannotStartPLC; + } + } + }; + return Result; +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::opCopyRamToRom() +{ + PReqFunCopyRamToRom ReqParams; + PResFunCtrl ResParams; + PS7ResHeader23 Answer; + int IsoSize, CurTimeout, Result; + char _modu[] = {'_','M','O','D','U'}; + + // Setup pointers (note : PDUH_out and PDU.Payload are the same pointer) + ReqParams=PReqFunCopyRamToRom(pbyte(PDUH_out)+sizeof(TS7ReqHeader)); + Answer =PS7ResHeader23(&PDU.Payload); + ResParams=PResFunCtrl(pbyte(Answer)+ResHeaderSize23); + // Fill Header + PDUH_out->P=0x32; // Always 0x32 + PDUH_out->PDUType=PduType_request; // 0x01 + PDUH_out->AB_EX=0x0000; // Always 0x0000 + PDUH_out->Sequence=GetNextWord(); // AutoInc + PDUH_out->ParLen=SwapWord(sizeof(TReqFunCopyRamToRom)); + PDUH_out->DataLen=0x0000; // No Data + // Fill Params + ReqParams->Fun=pduControl; + ReqParams->Uk_7[0]=0x00; + ReqParams->Uk_7[1]=0x00; + ReqParams->Uk_7[2]=0x00; + ReqParams->Uk_7[3]=0x00; + ReqParams->Uk_7[4]=0x00; + ReqParams->Uk_7[5]=0x00; + ReqParams->Uk_7[6]=0xFD; + + ReqParams->Len_1=SwapWord(0x0002); + ReqParams->SFun =SwapWord(0x4550); + ReqParams->Len_2=0x05; + memcpy(ReqParams->Cmd,&_modu,5); + + IsoSize=sizeof(TS7ReqHeader)+sizeof(TReqFunCopyRamToRom); + // Changes the timeout + CurTimeout=RecvTimeout; + RecvTimeout=Job.IParam; + Result=isoExchangeBuffer(0,IsoSize); + // Restores the timeout + RecvTimeout=CurTimeout; + + if (Result==0) + { + if ((Answer->Error!=0) || (ResParams->ResFun!=pduControl)) + Result=errCliCannotCopyRamToRom; + } + return Result; +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::opCompress() +{ + PReqFunCompress ReqParams; + PResFunCtrl ResParams; + PS7ResHeader23 Answer; + int IsoSize, CurTimeout, Result; + char _garb[] = {'_','G','A','R','B'}; + + // Setup pointers (note : PDUH_out and PDU.Payload are the same pointer) + ReqParams=PReqFunCompress(pbyte(PDUH_out)+sizeof(TS7ReqHeader)); + Answer =PS7ResHeader23(&PDU.Payload); + ResParams=PResFunCtrl(pbyte(Answer)+ResHeaderSize23); + // Fill Header + PDUH_out->P=0x32; // Always 0x32 + PDUH_out->PDUType=PduType_request; // 0x01 + PDUH_out->AB_EX=0x0000; // Always 0x0000 + PDUH_out->Sequence=GetNextWord(); // AutoInc + PDUH_out->ParLen=SwapWord(sizeof(TReqFunCompress)); + PDUH_out->DataLen=0x0000; // No Data + // Fill Params + ReqParams->Fun=pduControl; + ReqParams->Uk_7[0]=0x00; + ReqParams->Uk_7[1]=0x00; + ReqParams->Uk_7[2]=0x00; + ReqParams->Uk_7[3]=0x00; + ReqParams->Uk_7[4]=0x00; + ReqParams->Uk_7[5]=0x00; + ReqParams->Uk_7[6]=0xFD; + + ReqParams->Len_1=0x0000; + ReqParams->Len_2=0x05; + memcpy(ReqParams->Cmd,&_garb,5); + + IsoSize=sizeof(TS7ReqHeader)+sizeof(TReqFunCompress); + // Changes the timeout + CurTimeout=RecvTimeout; + RecvTimeout=Job.IParam; + Result=isoExchangeBuffer(0,IsoSize); + // Restores the timeout + RecvTimeout=CurTimeout; + + if (Result==0) + { + if (((Answer->Error!=0) || (ResParams->ResFun!=pduControl))) + Result=errCliCannotCompress; + } + return Result; +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::opGetProtection() +{ + PS7Protection Info, usrInfo; + int Result; + + // Store Pointer + usrInfo=PS7Protection(Job.pData); + memset(usrInfo, 0, sizeof(TS7Protection)); + + Job.ID =0x0232; + Job.Index =0x0004; + Job.IParam=0; // No copy in Usr Data pointed by Job.pData + Result =opReadSZL(); + if (Result==0) + { + Info=PS7Protection(pbyte(&opData)+6); + usrInfo->sch_schal=SwapWord(Info->sch_schal); + usrInfo->sch_par =SwapWord(Info->sch_par); + usrInfo->sch_rel =SwapWord(Info->sch_rel); + usrInfo->bart_sch =SwapWord(Info->bart_sch); + usrInfo->anl_sch =SwapWord(Info->anl_sch); + } + return Result; +} +//****************************************************************************** +// NOTE +// PASSWORD HACKING IS VERY FAR FROM THE AIM OF THIS PROJECT +// NEXT FUNCTION ONLY ENCODES THE ASCII PASSWORD TO BE DOWNLOADED IN THE PLC. +// +// MOREOVER **YOU NEED TO KNOW** THE CORRECT PASSWORD TO MEET THE CPU +// SECURITY LEVEL +//****************************************************************************** +int TSnap7MicroClient::opSetPassword() +{ + PReqFunSecurity ReqParams; + PReqDataSecurity ReqData; + PResParamsSecurity ResParams; + PS7ResHeader23 Answer; + int c, IsoSize, Result; + + // Setup pointers (note : PDUH_out and PDU.Payload are the same pointer) + ReqParams=PReqFunSecurity(pbyte(PDUH_out)+sizeof(TS7ReqHeader)); + ReqData =PReqDataSecurity(pbyte(ReqParams)+sizeof(TReqFunSecurity)); + Answer =PS7ResHeader23(&PDU.Payload); + ResParams=PResParamsSecurity(pbyte(Answer)+ResHeaderSize17); + // Fill Header + PDUH_out->P=0x32; // Always 0x32 + PDUH_out->PDUType=PduType_userdata; // 0x07 + PDUH_out->AB_EX=0x0000; // Always 0x0000 + PDUH_out->Sequence=GetNextWord(); // AutoInc + PDUH_out->ParLen =SwapWord(sizeof(TReqFunSecurity)); + PDUH_out->DataLen=SwapWord(sizeof(TReqDataSecurity)); + // Fill params (mostly constants) + ReqParams->Head[0]=0x00; + ReqParams->Head[1]=0x01; + ReqParams->Head[2]=0x12; + ReqParams->Plen =0x04; + ReqParams->Uk =0x11; + ReqParams->Tg =grSecurity; + ReqParams->SubFun =SFun_EnterPwd; + ReqParams->Seq =0x00; + // Fill Data + ReqData->Ret =0xFF; + ReqData->TS =TS_ResOctet; + ReqData->DLen =SwapWord(0x0008); // 8 bytes data : password + // Encode the password + ReqData->Pwd[0]=opData[0] ^ 0x55; + ReqData->Pwd[1]=opData[1] ^ 0x55; + for (c = 2; c < 8; c++){ + ReqData->Pwd[c]=opData[c] ^ 0x55 ^ ReqData->Pwd[c-2]; + }; + + IsoSize=sizeof(TS7ReqHeader)+sizeof(TReqFunSecurity)+sizeof(TReqDataSecurity); + Result=isoExchangeBuffer(0,IsoSize); + + // Get Return + if (Result==0) + { + if (ResParams->Err!=0) + Result=CpuError(SwapWord(ResParams->Err)); + }; + + return Result; +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::opClearPassword() +{ + PReqFunSecurity ReqParams; + PReqDataSecurity ReqData; + PResParamsSecurity ResParams; + PS7ResHeader23 Answer; + int IsoSize, Result; + + // Setup pointers (note : PDUH_out and PDU.Payload are the same pointer) + ReqParams=PReqFunSecurity(pbyte(PDUH_out)+sizeof(TS7ReqHeader)); + ReqData =PReqDataSecurity(pbyte(ReqParams)+sizeof(TReqFunSecurity)); + Answer =PS7ResHeader23(&PDU.Payload); + ResParams=PResParamsSecurity(pbyte(Answer)+ResHeaderSize17); + // Fill Header + PDUH_out->P=0x32; // Always 0x32 + PDUH_out->PDUType=PduType_userdata; // 0x07 + PDUH_out->AB_EX=0x0000; // Always 0x0000 + PDUH_out->Sequence=GetNextWord(); // AutoInc + PDUH_out->ParLen =SwapWord(sizeof(TReqFunSecurity)); + PDUH_out->DataLen=SwapWord(0x0004); // We need only 4 bytes + // Fill params (mostly constants) + ReqParams->Head[0]=0x00; + ReqParams->Head[1]=0x01; + ReqParams->Head[2]=0x12; + ReqParams->Plen =0x04; + ReqParams->Uk =0x11; + ReqParams->Tg =grSecurity; + ReqParams->SubFun =SFun_CancelPwd; + ReqParams->Seq =0x00; + // Fill Data + ReqData->Ret =0x0A; + ReqData->TS =0x00; + ReqData->DLen =0x0000; + + IsoSize=sizeof(TS7ReqHeader)+sizeof(TReqFunSecurity)+4; + Result=isoExchangeBuffer(0,IsoSize); + + // Get Return + if (Result==0) + { + if (ResParams->Err!=0) + Result=CpuError(SwapWord(ResParams->Err)); + }; + return Result; +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::CpuError(int Error) +{ + switch(Error) + { + case 0 : return 0; + case Code7AddressOutOfRange : return errCliAddressOutOfRange; + case Code7InvalidTransportSize : return errCliInvalidTransportSize; + case Code7WriteDataSizeMismatch : return errCliWriteDataSizeMismatch; + case Code7ResItemNotAvailable : + case Code7ResItemNotAvailable1 : return errCliItemNotAvailable; + case Code7DataOverPDU : return errCliSizeOverPDU; + case Code7InvalidValue : return errCliInvalidValue; + case Code7FunNotAvailable : return errCliFunNotAvailable; + case Code7NeedPassword : return errCliNeedPassword; + case Code7InvalidPassword : return errCliInvalidPassword; + case Code7NoPasswordToSet : + case Code7NoPasswordToClear : return errCliNoPasswordToSetOrClear; + default: + return errCliFunctionRefused; + }; +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::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; + } +} +//--------------------------------------------------------------------------- +longword TSnap7MicroClient::DWordAt(void * P) +{ + longword DW; + DW=*(longword*)P; + return SwapDWord(DW); +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::CheckBlock(int BlockType, int BlockNum, void * pBlock, int Size) +{ + PS7CompactBlockInfo Info = PS7CompactBlockInfo(pBlock); + + if (BlockType>=0) // if (BlockType<0 the test is skipped + { + if ((BlockType!=Block_OB)&&(BlockType!=Block_DB)&&(BlockType!=Block_FB)&& + (BlockType!=Block_FC)&&(BlockType!=Block_SDB)&&(BlockType!=Block_SFC)&& + (BlockType!=Block_SFB)) + return errCliInvalidBlockType; + } + + if (BlockNum>=0) // if (BlockNum<0 the test is skipped + { + if (BlockNum>0xFFFF) + return errCliInvalidBlockNumber; + }; + + if (SwapDWord(Info->LenLoadMem)!=longword(Size)) + return errCliInvalidBlockSize; + + // Check the presence of the footer + if (SwapWord(Info->MC7Len)+sizeof(TS7CompactBlockInfo)>=u_int(Size)) + return errCliInvalidBlockSize; + + return 0; +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::SubBlockToBlock(int SBB) +{ + switch (SBB) + { + case SubBlk_OB : return Block_OB; + case SubBlk_DB : return Block_DB; + case SubBlk_SDB : return Block_SDB; + case SubBlk_FC : return Block_FC; + case SubBlk_SFC : return Block_SFC; + case SubBlk_FB : return Block_FB; + case SubBlk_SFB : return Block_SFB; + default : return 0; + }; +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::PerformOperation() +{ + ClrError(); + int Operation=Job.Op; + switch(Operation) + { + case s7opNone: + Job.Result=errCliInvalidParams; + break; + case s7opReadArea: + Job.Result=opReadArea(); + break; + case s7opWriteArea: + Job.Result=opWriteArea(); + break; + case s7opReadMultiVars: + Job.Result=opReadMultiVars(); + break; + case s7opWriteMultiVars: + Job.Result=opWriteMultiVars(); + break; + case s7opDBGet: + Job.Result=opDBGet(); + break; + case s7opDBFill: + Job.Result=opDBFill(); + break; + case s7opUpload: + Job.Result=opUpload(); + break; + case s7opDownload: + Job.Result=opDownload(); + break; + case s7opDelete: + Job.Result=opDelete(); + break; + case s7opListBlocks: + Job.Result=opListBlocks(); + break; + case s7opAgBlockInfo: + Job.Result=opAgBlockInfo(); + break; + case s7opListBlocksOfType: + Job.Result=opListBlocksOfType(); + break; + case s7opReadSzlList: + Job.Result=opReadSZLList(); + break; + case s7opReadSZL: + Job.Result=opReadSZL(); + break; + case s7opGetDateTime: + Job.Result=opGetDateTime(); + break; + case s7opSetDateTime: + Job.Result=opSetDateTime(); + break; + case s7opGetOrderCode: + Job.Result=opGetOrderCode(); + break; + case s7opGetCpuInfo: + Job.Result=opGetCpuInfo(); + break; + case s7opGetCpInfo: + Job.Result=opGetCpInfo(); + break; + case s7opGetPlcStatus: + Job.Result=opGetPlcStatus(); + break; + case s7opPlcHotStart: + Job.Result=opPlcHotStart(); + break; + case s7opPlcColdStart: + Job.Result=opPlcColdStart(); + break; + case s7opCopyRamToRom: + Job.Result=opCopyRamToRom(); + break; + case s7opCompress: + Job.Result=opCompress(); + break; + case s7opPlcStop: + Job.Result=opPlcStop(); + break; + case s7opGetProtection: + Job.Result=opGetProtection(); + break; + case s7opSetPassword: + Job.Result=opSetPassword(); + break; + case s7opClearPassword: + Job.Result=opClearPassword(); + break; + } + Job.Time =SysGetTick()-JobStart; + Job.Pending=false; + return SetError(Job.Result); +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::Disconnect() +{ + JobStart=SysGetTick(); + PeerDisconnect(); + Job.Time=SysGetTick()-JobStart; + Job.Pending=false; + return 0; +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::Reset(bool DoReconnect) +{ + Job.Pending=false; + if (DoReconnect) { + Disconnect(); + return Connect(); + } + else + return 0; +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::Connect() +{ + int Result; + JobStart=SysGetTick(); + Result =PeerConnect(); + Job.Time=SysGetTick()-JobStart; + return Result; +} +//--------------------------------------------------------------------------- +void TSnap7MicroClient::SetConnectionType(word ConnType) +{ + ConnectionType=ConnType; +} +//--------------------------------------------------------------------------- +void TSnap7MicroClient::SetConnectionParams(const char *RemAddress, word LocalTSAP, word RemoteTSAP) +{ + SrcTSap = LocalTSAP; + DstTSap = RemoteTSAP; + strncpy(RemoteAddress, RemAddress, 16); +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::ConnectTo(const char *RemAddress, int Rack, int Slot) +{ + word RemoteTSAP = (ConnectionType<<8)+(Rack*0x20)+Slot; + SetConnectionParams(RemAddress, SrcTSap, RemoteTSAP); + return Connect(); +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::GetParam(int ParamNumber, void *pValue) +{ + switch (ParamNumber) + { + case p_u16_RemotePort: + *Puint16_t(pValue)=RemotePort; + break; + case p_i32_PingTimeout: + *Pint32_t(pValue)=PingTimeout; + break; + case p_i32_SendTimeout: + *Pint32_t(pValue)=SendTimeout; + break; + case p_i32_RecvTimeout: + *Pint32_t(pValue)=RecvTimeout; + break; + case p_i32_WorkInterval: + *Pint32_t(pValue)=WorkInterval; + break; + case p_u16_SrcRef: + *Puint16_t(pValue)=SrcRef; + break; + case p_u16_DstRef: + *Puint16_t(pValue)=DstRef; + break; + case p_u16_SrcTSap: + *Puint16_t(pValue)=SrcTSap; + break; + case p_i32_PDURequest: + *Pint32_t(pValue)=PDURequest; + break; + default: return errCliInvalidParamNumber; + } + return 0; +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::SetParam(int ParamNumber, void *pValue) +{ + switch (ParamNumber) + { + case p_u16_RemotePort: + if (!Connected) + RemotePort=*Puint16_t(pValue); + else + return errCliCannotChangeParam; + break; + case p_i32_PingTimeout: + PingTimeout=*Pint32_t(pValue); + break; + case p_i32_SendTimeout: + SendTimeout=*Pint32_t(pValue); + break; + case p_i32_RecvTimeout: + RecvTimeout=*Pint32_t(pValue); + break; + case p_i32_WorkInterval: + WorkInterval=*Pint32_t(pValue); + break; + case p_u16_SrcRef: + SrcRef=*Puint16_t(pValue); + break; + case p_u16_DstRef: + DstRef=*Puint16_t(pValue); + break; + case p_u16_SrcTSap: + SrcTSap=*Puint16_t(pValue); + break; + case p_i32_PDURequest: + PDURequest=*Pint32_t(pValue); + break; + default: return errCliInvalidParamNumber; + } + return 0; +} +//--------------------------------------------------------------------------- +// Data I/O functions +int TSnap7MicroClient::ReadArea(int Area, int DBNumber, int Start, int Amount, int WordLen, void * pUsrData) +{ + if (!Job.Pending) + { + Job.Pending = true; + Job.Op = s7opReadArea; + Job.Area = Area; + Job.Number = DBNumber; + Job.Start = Start; + Job.Amount = Amount; + Job.WordLen = WordLen; + Job.pData = pUsrData; + JobStart = SysGetTick(); + return PerformOperation(); + } + else + return SetError(errCliJobPending); +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::WriteArea(int Area, int DBNumber, int Start, int Amount, int WordLen, void * pUsrData) +{ + if (!Job.Pending) + { + Job.Pending = true; + Job.Op = s7opWriteArea; + Job.Area = Area; + Job.Number = DBNumber; + Job.Start = Start; + Job.Amount = Amount; + Job.WordLen = WordLen; + Job.pData = pUsrData; + JobStart = SysGetTick(); + return PerformOperation(); + } + else + return SetError(errCliJobPending); +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::ReadMultiVars(PS7DataItem Item, int ItemsCount) +{ + if (!Job.Pending) + { + Job.Pending =true; + Job.Op =s7opReadMultiVars; + Job.Amount =ItemsCount; + Job.pData =Item; + JobStart =SysGetTick(); + return PerformOperation(); + } + else + return SetError(errCliJobPending); +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::WriteMultiVars(PS7DataItem Item, int ItemsCount) +{ + if (!Job.Pending) + { + Job.Pending =true; + Job.Op =s7opWriteMultiVars; + Job.Amount =ItemsCount; + Job.pData =Item; + JobStart =SysGetTick(); + return PerformOperation(); + } + else + return SetError(errCliJobPending); +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::DBRead(int DBNumber, int Start, int Size, void * pUsrData) +{ + return ReadArea(S7AreaDB, DBNumber, Start, Size, S7WLByte, pUsrData); +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::DBWrite(int DBNumber, int Start, int Size, void * pUsrData) +{ + return WriteArea(S7AreaDB, DBNumber, Start, Size, S7WLByte, pUsrData); +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::MBRead(int Start, int Size, void * pUsrData) +{ + return ReadArea(S7AreaMK, 0, Start, Size, S7WLByte, pUsrData); +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::MBWrite(int Start, int Size, void * pUsrData) +{ + return WriteArea(S7AreaMK, 0, Start, Size, S7WLByte, pUsrData); +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::EBRead(int Start, int Size, void * pUsrData) +{ + return ReadArea(S7AreaPE, 0, Start, Size, S7WLByte, pUsrData); +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::EBWrite(int Start, int Size, void * pUsrData) +{ + return WriteArea(S7AreaPE, 0, Start, Size, S7WLByte, pUsrData); +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::ABRead(int Start, int Size, void * pUsrData) +{ + return ReadArea(S7AreaPA, 0, Start, Size, S7WLByte, pUsrData); +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::ABWrite(int Start, int Size, void * pUsrData) +{ + return WriteArea(S7AreaPA, 0, Start, Size, S7WLByte, pUsrData); +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::TMRead(int Start, int Amount, void * pUsrData) +{ + return ReadArea(S7AreaTM, 0, Start, Amount, S7WLTimer, pUsrData); +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::TMWrite(int Start, int Amount, void * pUsrData) +{ + return WriteArea(S7AreaTM, 0, Start, Amount, S7WLTimer, pUsrData); +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::CTRead(int Start, int Amount, void * pUsrData) +{ + return ReadArea(S7AreaCT, 0, Start, Amount, S7WLCounter, pUsrData); +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::CTWrite(int Start, int Amount, void * pUsrData) +{ + return WriteArea(S7AreaCT, 0, Start, Amount, S7WLCounter, pUsrData); +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::ListBlocks(PS7BlocksList pUsrData) +{ + if (!Job.Pending) + { + Job.Pending =true; + Job.Op =s7opListBlocks; + Job.pData =pUsrData; + JobStart =SysGetTick(); + return PerformOperation(); + } + else + return SetError(errCliJobPending); +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::GetAgBlockInfo(int BlockType, int BlockNum, PS7BlockInfo pUsrData) +{ + if (!Job.Pending) + { + Job.Pending =true; + Job.Op =s7opAgBlockInfo; + Job.Area =BlockType; + Job.Number =BlockNum; + Job.pData =pUsrData; + JobStart =SysGetTick(); + return PerformOperation(); + } + else + return SetError(errCliJobPending); +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::GetPgBlockInfo(void * pBlock, PS7BlockInfo pUsrData, int Size) +{ + PS7CompactBlockInfo Info; + PS7BlockFooter Footer; + + int Result=CheckBlock(-1,-1,pBlock,Size); + if (Result==0) + { + Info=PS7CompactBlockInfo(pBlock); + pUsrData->BlkType =Info->SubBlkType; + pUsrData->BlkNumber=SwapWord(Info->BlkNum); + pUsrData->BlkLang =Info->BlkLang; + pUsrData->BlkFlags =Info->BlkFlags; + pUsrData->MC7Size =SwapWord(Info->MC7Len); + pUsrData->LoadSize =SwapDWord(Info->LenLoadMem); + pUsrData->LocalData=SwapDWord(Info->LocDataLen); + pUsrData->SBBLength=SwapDWord(Info->SbbLen); + pUsrData->CheckSum =0; // this info is not available + pUsrData->Version =0; // this info is not available + FillTime(SwapWord(Info->CodeTime_dy),pUsrData->CodeDate); + FillTime(SwapWord(Info->IntfTime_dy),pUsrData->IntfDate); + + Footer=PS7BlockFooter(pbyte(Info)+pUsrData->LoadSize-sizeof(TS7BlockFooter)); + + memcpy(pUsrData->Author,Footer->Author,8); + memcpy(pUsrData->Family,Footer->Family,8); + memcpy(pUsrData->Header,Footer->Header,8); + }; + return SetError(Result); +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::ListBlocksOfType(int BlockType, TS7BlocksOfType *pUsrData, int &ItemsCount) +{ + if (!Job.Pending) + { + if (ItemsCount<1) + return SetError(errCliInvalidBlockSize); + Job.Pending =true; + Job.Op =s7opListBlocksOfType; + Job.Area =BlockType; + Job.pData =pUsrData; + Job.pAmount =&ItemsCount; + Job.Amount =ItemsCount; + JobStart =SysGetTick(); + return PerformOperation(); + } + else + return SetError(errCliJobPending); +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::Upload(int BlockType, int BlockNum, void * pUsrData, int & Size) +{ + if (!Job.Pending) + { + if (Size<=0) + return SetError(errCliInvalidBlockSize); + Job.Pending =true; + Job.Op =s7opUpload; + Job.Area =BlockType; + Job.pData =pUsrData; + Job.pAmount =&Size; + Job.Amount =Size; + Job.Number =BlockNum; + Job.IParam =0; // not full upload, only data + JobStart =SysGetTick(); + return PerformOperation(); + } + else + return SetError(errCliJobPending); +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::FullUpload(int BlockType, int BlockNum, void * pUsrData, int & Size) +{ + if (!Job.Pending) + { + if (Size<=0) + return SetError(errCliInvalidBlockSize); + Job.Pending =true; + Job.Op =s7opUpload; + Job.Area =BlockType; + Job.pData =pUsrData; + Job.pAmount =&Size; + Job.Amount =Size; + Job.Number =BlockNum; + Job.IParam =1; // header + data + footer + JobStart =SysGetTick(); + return PerformOperation(); + } + else + return SetError(errCliJobPending); +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::Download(int BlockNum, void * pUsrData, int Size) +{ + if (!Job.Pending) + { + Job.Pending =true; + Job.Op =s7opDownload; + memcpy(&opData, pUsrData, Size); + Job.Number =BlockNum; + Job.Amount =Size; + JobStart =SysGetTick(); + return PerformOperation(); + } + else + return SetError(errCliJobPending); +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::Delete(int BlockType, int BlockNum) +{ + if (!Job.Pending) + { + Job.Pending =true; + Job.Op =s7opDelete; + Job.Area =BlockType; + Job.Number =BlockNum; + JobStart =SysGetTick(); + return PerformOperation(); + } + else + return SetError(errCliJobPending); +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::DBGet(int DBNumber, void * pUsrData, int & Size) +{ + if (!Job.Pending) + { + if (Size<=0) + return SetError(errCliInvalidBlockSize); + Job.Pending =true; + Job.Op =s7opDBGet; + Job.Number =DBNumber; + Job.pData =pUsrData; + Job.pAmount =&Size; + Job.Amount =Size; + JobStart =SysGetTick(); + return PerformOperation(); + } + else + return SetError(errCliJobPending); +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::DBFill(int DBNumber, int FillChar) +{ + if (!Job.Pending) + { + Job.Pending =true; + Job.Op =s7opDBFill; + Job.Number =DBNumber; + Job.IParam =FillChar; + JobStart =SysGetTick(); + return PerformOperation(); + } + else + return SetError(errCliJobPending); +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::GetPlcDateTime(tm &DateTime) +{ + if (!Job.Pending) + { + Job.Pending =true; + Job.Op =s7opGetDateTime; + Job.pData =&DateTime; + JobStart =SysGetTick(); + return PerformOperation(); + } + else + return SetError(errCliJobPending); +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::SetPlcDateTime(tm * DateTime) +{ + if (!Job.Pending) + { + Job.Pending =true; + Job.Op =s7opSetDateTime; + Job.pData =DateTime; + JobStart =SysGetTick(); + return PerformOperation(); + } + else + return SetError(errCliJobPending); +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::SetPlcSystemDateTime() +{ + time_t Now; + time(&Now); + struct tm * DateTime = localtime (&Now); + return SetPlcDateTime(DateTime); +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::GetOrderCode(PS7OrderCode pUsrData) +{ + if (!Job.Pending) + { + Job.Pending =true; + Job.Op =s7opGetOrderCode; + Job.pData =pUsrData; + JobStart =SysGetTick(); + return PerformOperation(); + } + else + return SetError(errCliJobPending); +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::GetCpuInfo(PS7CpuInfo pUsrData) +{ + if (!Job.Pending) + { + Job.Pending =true; + Job.Op =s7opGetCpuInfo; + Job.pData =pUsrData; + JobStart =SysGetTick(); + return PerformOperation(); + } + else + return SetError(errCliJobPending); +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::GetCpInfo(PS7CpInfo pUsrData) +{ + if (!Job.Pending) + { + Job.Pending =true; + Job.Op =s7opGetCpInfo; + Job.pData =pUsrData; + JobStart =SysGetTick(); + return PerformOperation(); + } + else + return SetError(errCliJobPending); +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::ReadSZL(int ID, int Index, PS7SZL pUsrData, int &Size) +{ + if (!Job.Pending) + { + Job.Pending =true; + Job.Op =s7opReadSZL; + Job.ID =ID; + Job.Index =Index; + Job.pData =pUsrData; + Job.pAmount =&Size; + Job.Amount =Size; + Job.IParam =1; // Data has to be copied into user buffer + JobStart =SysGetTick(); + return PerformOperation(); + } + else + return SetError(errCliJobPending); +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::ReadSZLList(PS7SZLList pUsrData, int &ItemsCount) +{ + if (!Job.Pending) + { + Job.Pending =true; + Job.Op =s7opReadSzlList; + Job.pData =pUsrData; + Job.pAmount =&ItemsCount; + Job.Amount =ItemsCount; + JobStart =SysGetTick(); + return PerformOperation(); + } + else + return SetError(errCliJobPending); +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::PlcHotStart() +{ + if (!Job.Pending) + { + Job.Pending =true; + Job.Op =s7opPlcHotStart; + JobStart =SysGetTick(); + return PerformOperation(); + } + else + return SetError(errCliJobPending); +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::PlcColdStart() +{ + if (!Job.Pending) + { + Job.Pending =true; + Job.Op =s7opPlcColdStart; + JobStart =SysGetTick(); + return PerformOperation(); + } + else + return SetError(errCliJobPending); +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::PlcStop() +{ + if (!Job.Pending) + { + Job.Pending =true; + Job.Op =s7opPlcStop; + JobStart =SysGetTick(); + return PerformOperation(); + } + else + return SetError(errCliJobPending); +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::CopyRamToRom(int Timeout) +{ + if (!Job.Pending) + { + if (Timeout>0) + { + Job.Pending =true; + Job.Op =s7opCopyRamToRom; + Job.IParam =Timeout; + JobStart =SysGetTick(); + return PerformOperation(); + } + else + return SetError(errCliInvalidParams); + } + else + return SetError(errCliJobPending); +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::Compress(int Timeout) +{ + if (!Job.Pending) + { + if (Timeout>0) + { + Job.Pending =true; + Job.Op =s7opCompress; + Job.IParam =Timeout; + JobStart =SysGetTick(); + return PerformOperation(); + } + else + return SetError(errCliInvalidParams); + } + else + return SetError(errCliJobPending); +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::GetPlcStatus(int & Status) +{ + if (!Job.Pending) + { + Job.Pending =true; + Job.Op =s7opGetPlcStatus; + Job.pData =&Status; + JobStart =SysGetTick(); + return PerformOperation(); + } + else + return SetError(errCliJobPending); +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::GetProtection(PS7Protection pUsrData) +{ + if (!Job.Pending) + { + Job.Pending =true; + Job.Op =s7opGetProtection; + Job.pData =pUsrData; + JobStart =SysGetTick(); + return PerformOperation(); + } + else + return SetError(errCliJobPending); +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::SetSessionPassword(char *Password) +{ + if (!Job.Pending) + { + size_t L = strlen(Password); + // checks the len + if ((L<1) || (L>8)) + return SetError(errCliInvalidParams); + Job.Pending =true; + // prepares an 8 char string filled with spaces + memset(&opData,0x20,8); + // copies + strncpy((char*)&opData,Password,L); + Job.Op =s7opSetPassword; + JobStart =SysGetTick(); + return PerformOperation(); + } + else + return SetError(errCliJobPending); +} +//--------------------------------------------------------------------------- +int TSnap7MicroClient::ClearSessionPassword() +{ + if (!Job.Pending) + { + Job.Pending =true; + Job.Op =s7opClearPassword; + JobStart =SysGetTick(); + return PerformOperation(); + } + else + return SetError(errCliJobPending); +} +//--------------------------------------------------------------------------- + diff --git a/utils/snap7_src/src/core/s7_micro_client.h b/utils/snap7_src/src/core/s7_micro_client.h new file mode 100644 index 0000000..4d53619 --- /dev/null +++ b/utils/snap7_src/src/core/s7_micro_client.h @@ -0,0 +1,364 @@ +/*=============================================================================| +| 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/ | +|=============================================================================*/ +#ifndef s7_micro_client_h +#define s7_micro_client_h +//--------------------------------------------------------------------------- +#include "s7_peer.h" +//--------------------------------------------------------------------------- + +const longword errCliMask = 0xFFF00000; +const longword errCliBase = 0x000FFFFF; + +const longword errCliInvalidParams = 0x00200000; +const longword errCliJobPending = 0x00300000; +const longword errCliTooManyItems = 0x00400000; +const longword errCliInvalidWordLen = 0x00500000; +const longword errCliPartialDataWritten = 0x00600000; +const longword errCliSizeOverPDU = 0x00700000; +const longword errCliInvalidPlcAnswer = 0x00800000; +const longword errCliAddressOutOfRange = 0x00900000; +const longword errCliInvalidTransportSize = 0x00A00000; +const longword errCliWriteDataSizeMismatch = 0x00B00000; +const longword errCliItemNotAvailable = 0x00C00000; +const longword errCliInvalidValue = 0x00D00000; +const longword errCliCannotStartPLC = 0x00E00000; +const longword errCliAlreadyRun = 0x00F00000; +const longword errCliCannotStopPLC = 0x01000000; +const longword errCliCannotCopyRamToRom = 0x01100000; +const longword errCliCannotCompress = 0x01200000; +const longword errCliAlreadyStop = 0x01300000; +const longword errCliFunNotAvailable = 0x01400000; +const longword errCliUploadSequenceFailed = 0x01500000; +const longword errCliInvalidDataSizeRecvd = 0x01600000; +const longword errCliInvalidBlockType = 0x01700000; +const longword errCliInvalidBlockNumber = 0x01800000; +const longword errCliInvalidBlockSize = 0x01900000; +const longword errCliDownloadSequenceFailed = 0x01A00000; +const longword errCliInsertRefused = 0x01B00000; +const longword errCliDeleteRefused = 0x01C00000; +const longword errCliNeedPassword = 0x01D00000; +const longword errCliInvalidPassword = 0x01E00000; +const longword errCliNoPasswordToSetOrClear = 0x01F00000; +const longword errCliJobTimeout = 0x02000000; +const longword errCliPartialDataRead = 0x02100000; +const longword errCliBufferTooSmall = 0x02200000; +const longword errCliFunctionRefused = 0x02300000; +const longword errCliDestroying = 0x02400000; +const longword errCliInvalidParamNumber = 0x02500000; +const longword errCliCannotChangeParam = 0x02600000; + +const time_t DeltaSecs = 441763200; // Seconds between 1970/1/1 (C time base) and 1984/1/1 (Siemens base) + +#pragma pack(1) + +// Read/Write Multivars +typedef struct{ + int Area; + int WordLen; + int Result; + int DBNumber; + int Start; + int Amount; + void *pdata; +} TS7DataItem, *PS7DataItem; + +typedef int TS7ResultItems[MaxVars]; +typedef TS7ResultItems *PS7ResultItems; + +typedef struct { + int OBCount; + int FBCount; + int FCCount; + int SFBCount; + int SFCCount; + int DBCount; + int SDBCount; +} TS7BlocksList, *PS7BlocksList; + +typedef struct { + int BlkType; + int BlkNumber; + int BlkLang; + int BlkFlags; + int MC7Size; // The real size in bytes + int LoadSize; + int LocalData; + int SBBLength; + int CheckSum; + int Version; + // Chars info + char CodeDate[11]; + char IntfDate[11]; + char Author[9]; + char Family[9]; + char Header[9]; +} TS7BlockInfo, *PS7BlockInfo ; + +typedef word TS7BlocksOfType[0x2000]; +typedef TS7BlocksOfType *PS7BlocksOfType; + +typedef struct { + char Code[21]; // Order Code + byte V1; // Version V1.V2.V3 + byte V2; + byte V3; +} TS7OrderCode, *PS7OrderCode; + +typedef struct { + char ModuleTypeName[33]; + char SerialNumber[25]; + char ASName[25]; + char Copyright[27]; + char ModuleName[25]; +} TS7CpuInfo, *PS7CpuInfo; + +typedef struct { + int MaxPduLengt; + int MaxConnections; + int MaxMpiRate; + int MaxBusRate; +} TS7CpInfo, *PS7CpInfo; + +// See §33.1 of "System Software for S7-300/400 System and Standard Functions" +// and see SFC51 description too +typedef struct { + word LENTHDR; + word N_DR; +} SZL_HEADER, *PSZL_HEADER; + +typedef struct { + SZL_HEADER Header; + byte Data[0x4000-4]; +} TS7SZL, *PS7SZL; + +// SZL List of available SZL IDs : same as SZL but List items are big-endian adjusted +typedef struct { + SZL_HEADER Header; + word List[0x2000-2]; +} TS7SZLList, *PS7SZLList; + +// See §33.19 of "System Software for S7-300/400 System and Standard Functions" +typedef struct { + word sch_schal; + word sch_par; + word sch_rel; + word bart_sch; + word anl_sch; +} TS7Protection, *PS7Protection; + +#define s7opNone 0 +#define s7opReadArea 1 +#define s7opWriteArea 2 +#define s7opReadMultiVars 3 +#define s7opWriteMultiVars 4 +#define s7opDBGet 5 +#define s7opUpload 6 +#define s7opDownload 7 +#define s7opDelete 8 +#define s7opListBlocks 9 +#define s7opAgBlockInfo 10 +#define s7opListBlocksOfType 11 +#define s7opReadSzlList 12 +#define s7opReadSZL 13 +#define s7opGetDateTime 14 +#define s7opSetDateTime 15 +#define s7opGetOrderCode 16 +#define s7opGetCpuInfo 17 +#define s7opGetCpInfo 18 +#define s7opGetPlcStatus 19 +#define s7opPlcHotStart 20 +#define s7opPlcColdStart 21 +#define s7opCopyRamToRom 22 +#define s7opCompress 23 +#define s7opPlcStop 24 +#define s7opGetProtection 25 +#define s7opSetPassword 26 +#define s7opClearPassword 27 +#define s7opDBFill 28 + +// Param Number (to use with setparam) + +// Low level : change them to experiment new connections, their defaults normally work well +const int pc_iso_SendTimeout = 6; +const int pc_iso_RecvTimeout = 7; +const int pc_iso_ConnTimeout = 8; +const int pc_iso_SrcRef = 1; +const int pc_iso_DstRef = 2; +const int pc_iso_SrcTSAP = 3; +const int pc_iso_DstTSAP = 4; +const int pc_iso_IsoPduSize = 5; + +// Client Connection Type +const word CONNTYPE_PG = 0x01; // Connect to the PLC as a PG +const word CONNTYPE_OP = 0x02; // Connect to the PLC as an OP +const word CONNTYPE_BASIC = 0x03; // Basic connection + +#pragma pack() + +// Internal struct for operations +// Commands are not executed directly in the function such as "DBRead(...", +// but this struct is filled and then PerformOperation() is called. +// This allow us to implement async function very easily. + +struct TSnap7Job +{ + int Op; // Operation Code + int Result; // Operation result + bool Pending; // A Job is pending + longword Time; // Job Execution time + // Read/Write + int Area; // Also used for Block type and Block of type + int Number; // Used for DB Number, Block number + int Start; // Offset start + int WordLen; // Word length + // SZL + int ID; // SZL ID + int Index; // SZL Index + // ptr info + void * pData; // User data pointer + int Amount; // Items amount/Size in input + int *pAmount; // Items amount/Size in output + // Generic + int IParam; // Used for full upload and CopyRamToRom extended timeout +}; + +class TSnap7MicroClient: public TSnap7Peer +{ +private: + void FillTime(word SiemensTime, char *PTime); + byte BCDtoByte(byte B); + byte WordToBCD(word Value); + int opReadArea(); + int opWriteArea(); + int opReadMultiVars(); + int opWriteMultiVars(); + int opListBlocks(); + int opListBlocksOfType(); + int opAgBlockInfo(); + int opDBGet(); + int opDBFill(); + int opUpload(); + int opDownload(); + int opDelete(); + int opReadSZL(); + int opReadSZLList(); + int opGetDateTime(); + int opSetDateTime(); + int opGetOrderCode(); + int opGetCpuInfo(); + int opGetCpInfo(); + int opGetPlcStatus(); + int opPlcStop(); + int opPlcHotStart(); + int opPlcColdStart(); + int opCopyRamToRom(); + int opCompress(); + int opGetProtection(); + int opSetPassword(); + int opClearPassword(); + int CpuError(int Error); + longword DWordAt(void * P); + int CheckBlock(int BlockType, int BlockNum, void *pBlock, int Size); + int SubBlockToBlock(int SBB); +protected: + word ConnectionType; + longword JobStart; + TSnap7Job Job; + int DataSizeByte(int WordLength); + int opSize; // last operation size + int PerformOperation(); +public: + TS7Buffer opData; + TSnap7MicroClient(); + ~TSnap7MicroClient(); + int Reset(bool DoReconnect); + void SetConnectionParams(const char *RemAddress, word LocalTSAP, word RemoteTsap); + void SetConnectionType(word ConnType); + int ConnectTo(const char *RemAddress, int Rack, int Slot); + int Connect(); + int Disconnect(); + int GetParam(int ParamNumber, void *pValue); + int SetParam(int ParamNumber, void *pValue); + // Fundamental Data I/O functions + int ReadArea(int Area, int DBNumber, int Start, int Amount, int WordLen, void * pUsrData); + int WriteArea(int Area, int DBNumber, int Start, int Amount, int WordLen, void * pUsrData); + int ReadMultiVars(PS7DataItem Item, int ItemsCount); + int WriteMultiVars(PS7DataItem Item, int ItemsCount); + // Data I/O Helper functions + int DBRead(int DBNumber, int Start, int Size, void * pUsrData); + int DBWrite(int DBNumber, int Start, int Size, void * pUsrData); + int MBRead(int Start, int Size, void * pUsrData); + int MBWrite(int Start, int Size, void * pUsrData); + int EBRead(int Start, int Size, void * pUsrData); + int EBWrite(int Start, int Size, void * pUsrData); + int ABRead(int Start, int Size, void * pUsrData); + int ABWrite(int Start, int Size, void * pUsrData); + int TMRead(int Start, int Amount, void * pUsrData); + int TMWrite(int Start, int Amount, void * pUsrData); + int CTRead(int Start, int Amount, void * pUsrData); + int CTWrite(int Start, int Amount, void * pUsrData); + // Directory functions + int ListBlocks(PS7BlocksList pUsrData); + int GetAgBlockInfo(int BlockType, int BlockNum, PS7BlockInfo pUsrData); + int GetPgBlockInfo(void * pBlock, PS7BlockInfo pUsrData, int Size); + int ListBlocksOfType(int BlockType, TS7BlocksOfType *pUsrData, int & ItemsCount); + // Blocks functions + int Upload(int BlockType, int BlockNum, void * pUsrData, int & Size); + int FullUpload(int BlockType, int BlockNum, void * pUsrData, int & Size); + int Download(int BlockNum, void * pUsrData, int Size); + int Delete(int BlockType, int BlockNum); + int DBGet(int DBNumber, void * pUsrData, int & Size); + int DBFill(int DBNumber, int FillChar); + // Date/Time functions + int GetPlcDateTime(tm &DateTime); + int SetPlcDateTime(tm * DateTime); + int SetPlcSystemDateTime(); + // System Info functions + int GetOrderCode(PS7OrderCode pUsrData); + int GetCpuInfo(PS7CpuInfo pUsrData); + int GetCpInfo(PS7CpInfo pUsrData); + int ReadSZL(int ID, int Index, PS7SZL pUsrData, int &Size); + int ReadSZLList(PS7SZLList pUsrData, int &ItemsCount); + // Control functions + int PlcHotStart(); + int PlcColdStart(); + int PlcStop(); + int CopyRamToRom(int Timeout); + int Compress(int Timeout); + int GetPlcStatus(int &Status); + // Security functions + int GetProtection(PS7Protection pUsrData); + int SetSessionPassword(char *Password); + int ClearSessionPassword(); + // Properties + bool Busy(){ return Job.Pending; }; + int Time(){ return int(Job.Time);} +}; + +typedef TSnap7MicroClient *PSnap7MicroClient; + +//--------------------------------------------------------------------------- +#endif // s7_micro_client_h diff --git a/utils/snap7_src/src/core/s7_partner.cpp b/utils/snap7_src/src/core/s7_partner.cpp new file mode 100644 index 0000000..befe642 --- /dev/null +++ b/utils/snap7_src/src/core/s7_partner.cpp @@ -0,0 +1,1178 @@ +/*=============================================================================| +| PROJECT SNAP7 1.3.0 | +|==============================================================================| +| Copyright (C) 2013, 2015 Davide Nardella | +| All rights reserved. | +|==============================================================================| +| SNAP7 is free software: you can redistribute it and/or modify | +| it under the terms of the Lesser GNU General Public License as published by | +| the Free Software Foundation, either version 3 of the License, or | +| (at your option) any later version. | +| | +| It means that you can distribute your commercial software linked with | +| SNAP7 without the requirement to distribute the source code of your | +| application and without the requirement that your application be itself | +| distributed under LGPL. | +| | +| SNAP7 is distributed in the hope that it will be useful, | +| but WITHOUT ANY WARRANTY; without even the implied warranty of | +| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | +| Lesser GNU General Public License for more details. | +| | +| You should have received a copy of the GNU General Public License and a | +| copy of Lesser GNU General Public License along with Snap7. | +| If not, see http://www.gnu.org/licenses/ | +|=============================================================================*/ +#include "s7_partner.h" +//------------------------------------------------------------------------------ + +static PServersManager ServersManager = NULL; + +//------------------------------------------------------------------------------ +int ServersManager_GetServer(longword BindAddress, PConnectionServer &Server) +{ + if (ServersManager == NULL) + { + ServersManager = new TServersManager(); + } + return ServersManager->GetServer(BindAddress, Server); +} +//------------------------------------------------------------------------------ +void ServersManager_RemovePartner(PConnectionServer Server, PSnap7Partner Partner) +{ + if (ServersManager != NULL) + { + ServersManager->RemovePartner(Server, Partner); + if (ServersManager->ServersCount==0) + { + delete ServersManager; + ServersManager = NULL; + } + } +} +//------------------------------------------------------------------------------ +// CONNECTION SERVERS MANAGER +//------------------------------------------------------------------------------ +TServersManager::TServersManager() +{ + cs = new TSnapCriticalSection; + memset(Servers,0,sizeof(Servers)); + ServersCount=0; +} +//--------------------------------------------------------------------------- +TServersManager::~TServersManager() +{ + int c; + Lock(); + if (ServersCount>0) + { + for (c = 0; c < MaxAdapters; c++) + { + if (Servers[c]!=0) + { + delete Servers[c]; + Servers[c]=0; + ServersCount--; + } + } + } + Unlock(); + delete cs; +} +//--------------------------------------------------------------------------- +void TServersManager::Lock() +{ + cs->Enter(); +} +//--------------------------------------------------------------------------- +void TServersManager::Unlock() +{ + cs->Leave(); +} +//--------------------------------------------------------------------------- +void TServersManager::AddServer(PConnectionServer Server) +{ + int c; + Lock(); + for (c = 0; c < MaxAdapters; c++) + { + if (Servers[c]==0) + { + Servers[c]=Server; + ServersCount++; + break; + } + } + Unlock(); +} +//--------------------------------------------------------------------------- +int TServersManager::CreateServer(longword BindAddress, PConnectionServer &Server) +{ + in_addr sin; + sin.s_addr=BindAddress; + int Result; + + if (ServersCountStartTo(inet_ntoa(sin)); + if (Result!=0) + { + delete Server; + Server=0; + return Result; + } + AddServer(Server); + return 0; + } + else + return errServerNoRoom; +} +//--------------------------------------------------------------------------- +int TServersManager::GetServer(longword BindAddress, PConnectionServer &Server) +{ + int c; + Server=0; + for (c = 0; c < ServersCount; c++) + { + if (Servers[c]->LocalBind==BindAddress) + { + Server=Servers[c]; + break; + } + } + if (Server==0) + return CreateServer(BindAddress, Server); + else + return 0; +} +//--------------------------------------------------------------------------- +void TServersManager::RemovePartner(PConnectionServer Server, PSnap7Partner Partner) +{ + int c; + Server->RemovePartner(Partner); + if (Server->PartnersCount==0) + { + Lock(); + for (c = 0; c < MaxAdapters; c++) + { + if (Servers[c]==Server) + { + Servers[c]=0; + ServersCount--; + break; + } + } + Unlock(); + delete Server; + } +} +//--------------------------------------------------------------------------- +// CONNECTION SERVER +//------------------------------------------------------------------------------ +void TConnListenerThread::Execute() +{ + socket_t Sock; + bool Valid; + + while (!Terminated) + { + if (FListener->CanRead(FListener->WorkInterval)) + { + Sock=FListener->SckAccept(); // in any case we must accept + Valid=Sock!=INVALID_SOCKET; + // check if we are not destroying + if ((!Terminated) && (!FServer->Destroying)) + { + if (Valid) + FServer->Incoming(Sock); + } + else + if (Valid) + Msg_CloseSocket(Sock); + }; + } +} +//------------------------------------------------------------------------------ +TConnectionServer::TConnectionServer() +{ + cs = new TSnapCriticalSection; + memset(Partners,0,sizeof(Partners)); + FRunning = false; + PartnersCount = 0; +} +//------------------------------------------------------------------------------ +TConnectionServer::~TConnectionServer() +{ + Stop(); + delete cs; +} +//--------------------------------------------------------------------------- +void TConnectionServer::Lock() +{ + cs->Enter(); +} +void TConnectionServer::Unlock() +{ + cs->Leave(); +} +//--------------------------------------------------------------------------- +int TConnectionServer::Start() +{ + int Result; + // Creates the listener + SockListener = new TMsgSocket(); + strncpy(SockListener->LocalAddress,FLocalAddress,16); + SockListener->LocalPort=isoTcpPort; + // Binds + Result=SockListener->SckBind(); + if (Result==0) + { + LocalBind=SockListener->LocalBind; + // Listen + Result=SockListener->SckListen(); + if (Result==0) + { + // Creates the Listener thread + ServerThread = new TConnListenerThread(SockListener, this); + ServerThread->Start(); + } + else + delete SockListener; + } + else + delete SockListener; + + FRunning=Result==0; + return Result; +} +//--------------------------------------------------------------------------- +int TConnectionServer::StartTo(const char *Address) +{ + strncpy(FLocalAddress,Address,16); + return Start(); +} +//--------------------------------------------------------------------------- +void TConnectionServer::Stop() +{ + if (FRunning) + { + // Kills the listener thread + ServerThread->Terminate(); + if (ServerThread->WaitFor(csTimeout)!=WAIT_OBJECT_0) + ServerThread->Kill(); + delete ServerThread; + // Kills the listener + delete SockListener; + FRunning = false; + } +} +//--------------------------------------------------------------------------- +PSnap7Partner TConnectionServer::FindPartner(longword Address) +{ + int c; + PSnap7Partner Result; + for (c = 0; c < MaxPartners; c++) + { + Result=Partners[c]; + if (Result!=NULL) + { + if (Result->PeerAddress==Address) + return Result; + } + } + return NULL; +} +//------------------------------------------------------------------------------ +int TConnectionServer::FirstFree() +{ + int i; + for (i = 0; i < MaxPartners; i++) + { + if (Partners[i]==0) + return i; + } + return -1; +} +//------------------------------------------------------------------------------ +int TConnectionServer::RegisterPartner(PSnap7Partner Partner) +{ + PSnap7Partner aPartner; + int idx; + // check if already exists a passive partner linked to the same peer address + aPartner=FindPartner(Partner->PeerAddress); + if (aPartner==NULL) + { + Lock(); + idx=FirstFree(); + if (idx>=0) + { + Partners[idx]=Partner; + PartnersCount++; + } + Unlock(); + if (idx>=0) + return 0; + else + return errParNoRoom; + } + else + return errParAddressInUse; +} +//------------------------------------------------------------------------------ +void TConnectionServer::RemovePartner(PSnap7Partner Partner) +{ + int c; + Lock(); + for (c = 0; c < MaxPartners; c++) + { + if (Partners[c]==Partner) + { + Partners[c]=0; + PartnersCount--; + break; + } + } + Unlock(); +} +//------------------------------------------------------------------------------ +void TConnectionServer::Incoming(socket_t Sock) +{ + longword Address; + PSnap7Partner Partner; + + Address=Msg_GetSockAddr(Sock); + // Looks for a partner that is waiting for a connection from this address + Lock(); + Partner=FindPartner(Address); + Unlock(); + // if partner exists must not be already connected : a partner can be connected + // with only one peer at time + if ((Partner!=NULL) && (!Partner->Stopping) && (!Partner->Connected)) + Partner->SetSocket(Sock); + else + Msg_CloseSocket(Sock); // we are not interested +} +//------------------------------------------------------------------------------ +// PARTHER THREAD +//------------------------------------------------------------------------------ +void TPartnerThread::Execute() +{ + longword TheTime; + + FKaElapsed=SysGetTick(); + while ((!Terminated) && (!FPartner->Destroying)) + { + // Check connection + while (!Terminated && !FPartner->Connected && !FPartner->Destroying) + { + if (!FPartner->ConnectToPeer()) + SysSleep(FRecoveryTime); + } + // Execution + if ((!Terminated) && (!FPartner->Destroying) && (!FPartner->Execute())) + SysSleep(FRecoveryTime); + // Keep Alive + if (!Terminated && (!FPartner->Destroying) && FPartner->Active && FPartner->Connected) + { + TheTime=SysGetTick(); + if (TheTime-FKaElapsed>FPartner->KeepAliveTime) + { + FKaElapsed=TheTime; + if (!FPartner->Ping(FPartner->RemoteAddress)) + FPartner->Disconnect(); + }; + }; + }; +} +//------------------------------------------------------------------------------ +// S7 PARTNER +//------------------------------------------------------------------------------ +TSnap7Partner::TSnap7Partner(bool CreateActive) +{ + // We skip RFC/ISO header, our PDU is the ISO payload + PDUH_in=PS7ReqHeader(&PDU.Payload); + FWorkerThread=0; + OnBRecv = 0; + OnBSend = 0; + Active=CreateActive; + SendEvt = new TSnapEvent(true); + RecvEvt = new TSnapEvent(true); + FSendPending = false; + FRecvPending = false; + memset(&FRecvStatus,0,sizeof(TRecvStatus)); + memset(&FRecvLast,0,sizeof(TRecvLast)); + FSendElapsed = 0; + Destroying = false; + // public + Linked =false; + Running =false; + BindError =false; + BRecvTimeout =3000; + BSendTimeout =3000; + RecoveryTime =500; + KeepAliveTime =5000; + NextByte =0; + PeerAddress =0; + SendTime =0; + RecvTime =0; + BytesSent =0; + BytesRecv =0; + SendErrors =0; + RecvErrors =0; +} +//------------------------------------------------------------------------------ +TSnap7Partner::~TSnap7Partner() +{ + Stop(); + OnBRecv = 0; + OnBSend = 0; + delete SendEvt; + delete RecvEvt; +} +//------------------------------------------------------------------------------ +byte TSnap7Partner::GetNextByte() +{ + NextByte++; + if (NextByte==0xFF) + NextByte=1; + return NextByte; +} +//------------------------------------------------------------------------------ +int TSnap7Partner::Start() +{ + int Result; + PeerAddress=inet_addr(RemoteAddress); + SrcAddress =inet_addr(LocalAddress); + if (!Running) + { + if (!Active) + { + Result=ServersManager_GetServer(SrcAddress,FServer); + if (Result==0) + FServer->RegisterPartner(this); + BindError=Result!=0; + } + else + { + Linked=PeerConnect()==0; + Result=0; // we need to create the worker thread even tough it's not linked + }; + // if ok create the worker thread + if (Result==0) + { + FWorkerThread = new TPartnerThread(this, RecoveryTime); + FWorkerThread->Start(); + } + } + else + Result=0; + + Running=Result==0; + + return Result; +} +//------------------------------------------------------------------------------ +int TSnap7Partner::StartTo(const char *LocAddress, const char *RemAddress, word LocTsap, word RemTsap) +{ + SrcTSap=LocTsap; + DstTSap=RemTsap; + strcpy(LocalAddress,LocAddress); + strcpy(RemoteAddress,RemAddress); + return Start(); +} +//------------------------------------------------------------------------------ +int TSnap7Partner::Stop() +{ + if (Running) + { + Stopping=true; // to prevent incoming connections + CloseWorker(); + if (!Active && (FServer!=0)) + ServersManager_RemovePartner(FServer, this); + if (Connected) + Disconnect(); + Running =false; + Stopping=false; + }; + BindError=false; + return 0; +} +//------------------------------------------------------------------------------ +void TSnap7Partner::Disconnect() +{ + PeerDisconnect(); + Linked=false; +} +//------------------------------------------------------------------------------ +int TSnap7Partner::GetParam(int ParamNumber, void * pValue) +{ + switch (ParamNumber) + { + case p_u16_LocalPort: + *Puint16_t(pValue)=LocalPort; + break; + case p_u16_RemotePort: + *Puint16_t(pValue)=RemotePort; + break; + case p_i32_PingTimeout: + *Pint32_t(pValue)=PingTimeout; + break; + case p_i32_SendTimeout: + *Pint32_t(pValue)=SendTimeout; + break; + case p_i32_RecvTimeout: + *Pint32_t(pValue)=RecvTimeout; + break; + case p_i32_WorkInterval: + *Pint32_t(pValue)=WorkInterval; + break; + case p_u16_SrcRef: + *Puint16_t(pValue)=SrcRef; + break; + case p_u16_DstRef: + *Puint16_t(pValue)=DstRef; + break; + case p_u16_SrcTSap: + *Puint16_t(pValue)=SrcTSap; + break; + case p_i32_PDURequest: + *Pint32_t(pValue)=PDURequest; + break; + case p_i32_BSendTimeout: + *Pint32_t(pValue)=BSendTimeout; + break; + case p_i32_BRecvTimeout: + *Pint32_t(pValue)=BRecvTimeout; + break; + case p_u32_RecoveryTime: + *Puint32_t(pValue)=RecoveryTime; + break; + case p_u32_KeepAliveTime: + *Puint32_t(pValue)=KeepAliveTime; + break; + default: return errParInvalidParamNumber; + } + return 0; +} +//------------------------------------------------------------------------------ +int TSnap7Partner::SetParam(int ParamNumber, void * pValue) +{ + switch (ParamNumber) + { + case p_u16_RemotePort: + if (!Connected && Active) + RemotePort=*Puint16_t(pValue); + else + return errParCannotChangeParam; + break; + case p_i32_PingTimeout: + PingTimeout=*Pint32_t(pValue); + break; + case p_i32_SendTimeout: + SendTimeout=*Pint32_t(pValue); + break; + case p_i32_RecvTimeout: + RecvTimeout=*Pint32_t(pValue); + break; + case p_i32_WorkInterval: + WorkInterval=*Pint32_t(pValue); + break; + case p_u16_SrcRef: + SrcRef=*Puint16_t(pValue); + break; + case p_u16_DstRef: + DstRef=*Puint16_t(pValue); + break; + case p_u16_SrcTSap: + SrcTSap=*Puint16_t(pValue); + break; + case p_i32_PDURequest: + PDURequest=*Pint32_t(pValue); + break; + case p_i32_BSendTimeout: + BSendTimeout=*Pint32_t(pValue); + break; + case p_i32_BRecvTimeout: + BRecvTimeout=*Pint32_t(pValue); + break; + case p_u32_RecoveryTime: + RecoveryTime=*Puint32_t(pValue); + break; + case p_u32_KeepAliveTime: + KeepAliveTime=*Puint32_t(pValue); + break; + default: return errParInvalidParamNumber; + } + return 0; +} +//------------------------------------------------------------------------------ +void TSnap7Partner::ClearRecv() +{ + memset(&FRecvStatus,0,sizeof(TRecvStatus)); + FRecvPending=false; +} +//------------------------------------------------------------------------------ +bool TSnap7Partner::ConnectToPeer() +{ + bool Result; + if (Active) + { + Result=PeerConnect()==0; // try to Connect + Linked=Result; + } + else + Result =false; // nothing : we are waiting for a connection + + return Result; +} +//------------------------------------------------------------------------------ +bool TSnap7Partner::PerformFunctionNegotiate() +{ + PReqFunNegotiateParams ReqParams; + PResFunNegotiateParams ResParams; + TS7Answer23 Answer; + int Size; + + // Setup pointers + ReqParams=PReqFunNegotiateParams(pbyte(PDUH_in)+sizeof(TS7ReqHeader)); + ResParams=PResFunNegotiateParams(pbyte(&Answer)+sizeof(TS7ResHeader23)); + // We are here only because we found a PduType_request, the partner can only + // handle Bs} requests and pdu negotiation requests. + // So, now we must check the incoming function + if (ReqParams->FunNegotiate!=pduNegotiate) + { + LastError=errParInvalidPDU; + return false; + }; + // Prepares the answer + Answer.Header.P=0x32; + Answer.Header.PDUType =0x03; + Answer.Header.AB_EX =0x0000; + Answer.Header.Sequence=PDUH_in->Sequence; + Answer.Header.ParLen =SwapWord(sizeof(TResFunNegotiateParams)); + Answer.Header.DataLen =0x0000; + Answer.Header.Error =0x0000; + // Params point at the } of the header + ResParams->FunNegotiate=pduNegotiate; + ResParams->Unknown=0x0; + // Checks PDU request length + if (SwapWord(ResParams->PDULength)>IsoPayload_Size) + ResParams->PDULength=SwapWord(IsoPayload_Size); + else + ResParams->PDULength=ReqParams->PDULength; + // We offer the same + ResParams->ParallelJobs_1=ReqParams->ParallelJobs_1; + ResParams->ParallelJobs_2=ReqParams->ParallelJobs_2; + // And store the value + PDULength=SwapWord(ResParams->PDULength); + // Sends the answer + Size=sizeof(TS7ResHeader23) + sizeof(TResFunNegotiateParams); + if (isoSendBuffer(&Answer, Size)!=0) + SetError(errParNegotiatingPDU); + + Linked=LastError==0; + return Linked; +} +//------------------------------------------------------------------------------ +void TSnap7Partner::CloseWorker() +{ + int Timeout; + if (FWorkerThread) + { + FWorkerThread->Terminate(); + if (FRecvPending || FSendPending) + Timeout=3000; + else + Timeout=1000; + + if (FWorkerThread->WaitFor(Timeout)!=WAIT_OBJECT_0) + FWorkerThread->Kill(); + try { + delete FWorkerThread; + } + catch (...){ + } + FWorkerThread=0; + } +} +//------------------------------------------------------------------------------ +bool TSnap7Partner::BlockSend() +{ + PBSendReqParams ReqParams; + PBSendReqParams ResParams; + PBsendRequestData DataSendReq; + int TotalSize; + int SentSize; + int Slice; + int MaxSlice; + uintptr_t Offset; + pbyte Source; + bool First, Last; + byte Seq_IN; + int TxIsoSize; + pbyte Data; + pword TotalPackSize; + int DataPtrOffset; + word Extra; + + ClrError(); + TotalSize=TxBuffer.Size; + SentSize =TotalSize; + Offset=0; + First =true; + Seq_IN=0x00; + + // With BSend we can transfer up to 32k (S7300) or 64k (S7400), but splitted + // into slice that cannot exced the PDU size negotiated (including various headers). + MaxSlice=PDULength-sizeof(TS7ReqHeader)-sizeof(TBSendParams)-sizeof(TBsendRequestData)-2; + + ReqParams=PBSendReqParams(pbyte(PDUH_out)+sizeof(TS7ReqHeader)); + ResParams=ReqParams; // pdu 7 is symmetrical + + while ((TotalSize>0) && (LastError==0)) + { + Source=pbyte(&TxBuffer.Data)+Offset; + Slice=TotalSize; + + if (Slice>MaxSlice) + Slice=MaxSlice; + + TotalSize-=Slice; + Offset+=Slice; + Last=TotalSize==0; + + // Prepare send + DataPtrOffset=sizeof(TS7ReqHeader)+sizeof(TBSendParams); + // Header + PDUH_out->P=0x32; // Always 0x32 + PDUH_out->PDUType=PduType_userdata; // 7 + PDUH_out->AB_EX=0x0000; // Always 0x0000 + PDUH_out->Sequence=GetNextWord(); // Autoinc + PDUH_out->ParLen=SwapWord(sizeof(TBSendParams)); // 16 bytes + + ReqParams->Head[0]=0x00; + ReqParams->Head[1]=0x01; + ReqParams->Head[2]=0x12; + ReqParams->Plen =0x08; // length from here up the end of the record + ReqParams->Uk =0x12; + ReqParams->Tg =grBSend; // 0x46 + ReqParams->SubFun =0x01; + ReqParams->Seq =Seq_IN; + ReqParams->Err =0x0000; + if (Last) + ReqParams->EoS =0x00; + else + ReqParams->EoS =0x01; + // Next byte is auto inc and not zero for partial sequences + // Is zero for lonely sequences. + if (First && Last) + ReqParams->IDSeq=0x00; + else + ReqParams->IDSeq=GetNextByte(); + + DataSendReq=PBsendRequestData(pbyte(PDUH_out)+DataPtrOffset); + if (First) + { + // in the first pdu, after data header there is the whole packet length + TotalPackSize=pword(pbyte(DataSendReq)+sizeof(TBsendRequestData)); + Data=pbyte(TotalPackSize)+sizeof(word); + *TotalPackSize=SwapWord(word(TxBuffer.Size)); + Extra=2; // extra bytes (total pack size indicator) + } + else + { + Data=pbyte(DataSendReq)+sizeof(TBsendRequestData); + Extra=0; + }; + + PDUH_out->DataLen=SwapWord(word(sizeof(TBsendRequestData))+Slice+Extra); + DataSendReq->Len =SwapWord(Slice+8+Extra); + TxIsoSize=Slice+sizeof(TS7ReqHeader)+sizeof(TBSendParams)+sizeof(TBsendRequestData)+Extra; + + DataSendReq->FF =0xFF; + DataSendReq->TRSize =TS_ResOctet; + DataSendReq->DHead[0]=0x12; + DataSendReq->DHead[1]=0x06; + DataSendReq->DHead[2]=0x13; + DataSendReq->DHead[3]=0x00; + DataSendReq->R_ID =SwapDWord(TxBuffer.R_ID); + memcpy(Data, Source ,Slice); + + if (isoExchangeBuffer(NULL, TxIsoSize)!=0) + SetError(errParSendingBlock); + + if (LastError==0) + { + Seq_IN=ResParams->Seq; + if (SwapWord(ResParams->Err)!=0) + LastError=errParSendRefused; + } + + if (First) + { + First =false; + MaxSlice+=2; // only in the first frame we have the extra info + }; + }; + + SendTime=SysGetTick()-FSendElapsed; + if (LastError==0) + BytesSent+=SentSize; + + return LastError==0; +} +//------------------------------------------------------------------------------ +bool TSnap7Partner::PickData() +{ + PBSendReqParams ReqParams; + PBSendReqParams ResParams; + PBSendResData ResData; + PBsendRequestData DataSendReq; + pbyte Source, Target; + pword TotalPackSize; + word Slice; + int AnswerLen; + + ClrError(); + // Setup pointers + ReqParams =PBSendReqParams(pbyte(PDUH_in)+sizeof(TS7ReqHeader)); + ResParams =ReqParams; // pdu 7 is symmetrical + DataSendReq=PBsendRequestData(pbyte(ReqParams)+sizeof(TBSendParams)); + + // Checks if PDU is a BSend request + if ((PDUH_in->PDUType!=PduType_userdata) || (ReqParams->Tg!=grBSend)) + { + LastError=errParInvalidPDU; + return false; + } + + if (FRecvStatus.First) + { + TotalPackSize=(word*)(pbyte(DataSendReq)+sizeof(TBsendRequestData)); + FRecvStatus.TotalLength=SwapWord(*TotalPackSize); + Source=pbyte(DataSendReq)+sizeof(TBsendRequestData)+2; + FRecvStatus.In_R_ID=SwapDWord(DataSendReq->R_ID); + FRecvStatus.Offset=0; + Slice=SwapWord(DataSendReq->Len)-10; + } + else { + Slice=SwapWord(DataSendReq->Len)-8; + Source=pbyte(DataSendReq)+sizeof(TBsendRequestData); + } + + FRecvStatus.Done=ReqParams->EoS==0x00; + + Target=pbyte(&RxBuffer)+FRecvStatus.Offset; + memcpy(Target, Source, Slice); + FRecvStatus.Offset+=Slice; + + ResData =PBSendResData(pbyte(ResParams)+sizeof(TBSendParams)); + // Send Answer + PDUH_out->ParLen =SwapWord(sizeof(TBSendParams)); + PDUH_out->DataLen =SwapWord(sizeof(TBSendResData)); + + ResParams->Head[0]=0x00; + ResParams->Head[1]=0x01; + ResParams->Head[2]=0x12; + ResParams->Plen =0x08; // length from here up the end of the record + ResParams->Uk =0x12; + ResParams->Tg =0x86; + ResParams->SubFun =0x01; + ResParams->Seq =FRecvStatus.Seq_Out; + ResParams->Err =0x0000; + ResParams->EoS =0x00; + ResParams->IDSeq =0x00; + + ResData->DHead[0] =0x0A; + ResData->DHead[1] =0x00; + ResData->DHead[2] =0x00; + ResData->DHead[3] =0x00; + + AnswerLen=sizeof(TS7ReqHeader)+sizeof(TBSendParams)+sizeof(TBSendResData); + if (isoSendBuffer(NULL,AnswerLen)!=0) + SetError(errParRecvingBlock); + + return LastError==0; +} +//------------------------------------------------------------------------------ +bool TSnap7Partner::BlockRecv() +{ + bool Result; + if (!FRecvPending) // Start sequence + { + FRecvPending=true; + FRecvStatus.First=true; + FRecvStatus.Done =false; + FRecvStatus.Seq_Out =GetNextByte(); + FRecvStatus.Elapsed =SysGetTick(); + FRecvLast.Done=false; + FRecvLast.Result=0; + FRecvLast.R_ID=0; + FRecvLast.Size=0; + RecvTime =0; + FRecvLast.Count++; + if (FRecvLast.Count==0xFFFFFFFF) + FRecvLast.Count=0; + }; + + Result=PickData(); + FRecvStatus.First=false; + + if (!Result || FRecvStatus.Done) + { + FRecvLast.Result=LastError; + if (Result) + { + BytesRecv+=FRecvStatus.TotalLength; + RecvTime=SysGetTick()-FRecvStatus.Elapsed; + FRecvLast.R_ID=FRecvStatus.In_R_ID; + FRecvLast.Size=FRecvStatus.TotalLength; + }; + RecvEvt->Set(); + if ((OnBRecv!=NULL) && !Destroying) + OnBRecv(FRecvUsrPtr, FRecvLast.Result, FRecvLast.R_ID, &RxBuffer, FRecvLast.Size); + FRecvLast.Done=true; + ClearRecv(); + }; + return Result; +} +//------------------------------------------------------------------------------ +bool TSnap7Partner::ConnectionConfirm() +{ + if (FRecvPending) + ClearRecv(); + IsoConfirmConnection(pdu_type_CC); // <- Connection confirm + return LastTcpError!=WSAECONNRESET; +} +//------------------------------------------------------------------------------ +int TSnap7Partner::Status() +{ + if (Running) + { + if (Linked) + { + if (FRecvPending) + return par_receiving; + else + if (FSendPending) + return par_sending; + else + return par_linked; + } + else + if (Active) + return par_connecting; + else + return par_waiting; + } + else{ + if ((!Active) && BindError) + return par_binderror; + else + return par_stopped; + } +} +//------------------------------------------------------------------------------ +bool TSnap7Partner::Execute() +{ + TPDUKind PduKind; + bool RTimeout; + bool Result =true; + + // Checks if there is something to send (and we are not receiving...) + if (FSendPending && !FRecvPending) + { + Result=BlockSend(); + SendEvt->Set(); + if ((OnBSend!=NULL) && (!Destroying)) + OnBSend(FSendUsrPtr, LastError); + FSendPending=false; + } + + if (Destroying) + return false; + + // Checks if there is something to recv + if (Result && CanRead(WorkInterval)) + { + // Peeks info and returns PDU Kind + isoRecvPDU(&PDU); + if (LastTcpError==0) + { + // First check valid data incoming (most likely situation) + IsoPeek(&PDU,PduKind); + if (PduKind==pkValidData) + { + if (PDUH_in->PDUType==PduType_request) + { + if (FRecvPending) + ClearRecv(); + Result=PerformFunctionNegotiate(); + } + else // Pdu type userdata + Result=BlockRecv(); + } + else + if (PduKind==pkConnectionRequest) + Result=ConnectionConfirm(); + else // nothing else + Purge(); + } + else + Result=false; + }; + + if (LastTcpError==WSAECONNRESET) + { + Result=false; + Linked=false; + } + else + if (!Result) + Disconnect(); + + // Check BRecv sequence timeout + RTimeout= FRecvPending && (SysGetTick()-FRecvStatus.Elapsed>longword(BRecvTimeout)); + + if (RTimeout) + { + LastError=errParFrameTimeout; + RecvEvt->Set(); + if ((OnBRecv!=NULL) && !Destroying) + OnBRecv(FRecvUsrPtr, LastError, 0, &RxBuffer,0); + }; + + if (!Result || RTimeout) + ClearRecv(); // parframetimeout + + return Result; +} + +//------------------------------------------------------------------------------ +int TSnap7Partner::BSend(longword R_ID, void *pUsrData, int Size) +{ + // The block send is managed into the worker thread. + // Sync Bsend consists of AsBSend+WaitAsCompletion + int Result=AsBSend(R_ID, pUsrData, Size); + if (Result==0) + Result=WaitAsBSendCompletion(BSendTimeout); + return Result; +} +//------------------------------------------------------------------------------ +int TSnap7Partner::AsBSend(longword R_ID, void *pUsrData, int Size) +{ + SendTime=0; + if (Linked) + { + if (!FSendPending) + { + memcpy(&TxBuffer.Data, pUsrData, Size); + TxBuffer.R_ID=R_ID; + TxBuffer.Size=Size; + SendEvt->Reset(); + FSendPending=true; + FSendElapsed=SysGetTick(); + return 0; + } + else + return errParBusy; + } + else + return SetError(errParNotLinked); +} +//------------------------------------------------------------------------------ +bool TSnap7Partner::CheckAsBSendCompletion(int &opResult) +{ + if (!Destroying) + { + if (!FSendPending) + opResult=LastError; + else + opResult=errParBusy; + + return !FSendPending; + } + else + { + opResult=errParDestroying; + return true; + } + +} +//------------------------------------------------------------------------------ +int TSnap7Partner::WaitAsBSendCompletion(longword Timeout) +{ + if (SendEvt->WaitFor(BSendTimeout)==WAIT_OBJECT_0) + { + if (!Destroying) + return LastError; + else + return SetError(errParDestroying); + } + else + return SetError(errParSendTimeout); +} +//------------------------------------------------------------------------------ +int TSnap7Partner::SetSendCallback(pfn_ParBSendCompletion pCompletion, void *usrPtr) +{ + OnBSend=pCompletion; + FSendUsrPtr=usrPtr; + return 0; +} +//------------------------------------------------------------------------------ +int TSnap7Partner::BRecv(longword &R_ID, void *pData, int &Size, longword Timeout) +{ + int Result=0; + if (RecvEvt->WaitFor(Timeout)==WAIT_OBJECT_0) + { + R_ID =FRecvLast.R_ID; + Size =FRecvLast.Size; + if (FRecvLast.Result==0) + { + if (pData!=NULL) + memcpy(pData, &RxBuffer, Size); + else + Result=errParInvalidParams; + } + else + Result=FRecvLast.Result; + RecvEvt->Reset(); + } + else + Result=errParRecvTimeout; + + return SetError(Result); +} +//------------------------------------------------------------------------------ +bool TSnap7Partner::CheckAsBRecvCompletion(int &opResult, longword &R_ID, + void *pData, int &Size) +{ + if (Destroying) + { + Size=0; + opResult=errParDestroying; + return true; + } + + bool Result=FRecvLast.Done; + if (Result) + { + Size=FRecvLast.Size; + R_ID=FRecvLast.R_ID; + opResult=FRecvLast.Result; + if ((pData!=NULL) && (Size>0) && (opResult==0)) + memcpy(pData, &RxBuffer, Size); + FRecvLast.Done=false; + } + return Result; +} +//------------------------------------------------------------------------------ +int TSnap7Partner::SetRecvCallback(pfn_ParBRecvCallBack pCompletion, void *usrPtr) +{ + OnBRecv=pCompletion; + FRecvUsrPtr=usrPtr; + return 0; +} +//------------------------------------------------------------------------------ + diff --git a/utils/snap7_src/src/core/s7_partner.h b/utils/snap7_src/src/core/s7_partner.h new file mode 100644 index 0000000..95211a2 --- /dev/null +++ b/utils/snap7_src/src/core/s7_partner.h @@ -0,0 +1,284 @@ +/*=============================================================================| +| 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/ | +|=============================================================================*/ +#ifndef s7_partner_h +#define s7_partner_h +//--------------------------------------------------------------------------- +#include "snap_threads.h" +#include "s7_peer.h" +//--------------------------------------------------------------------------- +using namespace std; +//--------------------------------------------------------------------------- + +#define MaxPartners 256 +#define MaxAdapters 256 +#define csTimeout 1500 // Connection server destruction timeout + +const int par_stopped = 0; // stopped +const int par_connecting = 1; // running and active connecting +const int par_waiting = 2; // running and waiting for a connection +const int par_linked = 3; // running and connected +const int par_sending = 4; // sending data +const int par_receiving = 5; // receiving data +const int par_binderror = 6; // error starting passive partner + +const longword errParMask = 0xFFF00000; +const longword errParBase = 0x000FFFFF; + +const longword errParAddressInUse = 0x00200000; +const longword errParNoRoom = 0x00300000; +const longword errServerNoRoom = 0x00400000; +const longword errParInvalidParams = 0x00500000; +const longword errParNotLinked = 0x00600000; +const longword errParBusy = 0x00700000; +const longword errParFrameTimeout = 0x00800000; +const longword errParInvalidPDU = 0x00900000; +const longword errParSendTimeout = 0x00A00000; +const longword errParRecvTimeout = 0x00B00000; +const longword errParSendRefused = 0x00C00000; +const longword errParNegotiatingPDU = 0x00D00000; +const longword errParSendingBlock = 0x00E00000; +const longword errParRecvingBlock = 0x00F00000; +const longword errParBindError = 0x01000000; +const longword errParDestroying = 0x01100000; +const longword errParInvalidParamNumber = 0x01200000; // Invalid param (par_get/set_param) +const longword errParCannotChangeParam = 0x01300000; // Cannot change because running +const longword errParBufferTooSmall = 0x01400000; // Raised by LabVIEW wrapper + +class TSnap7Partner; +typedef TSnap7Partner *PSnap7Partner; + +class TConnectionServer; +typedef TConnectionServer *PConnectionServer; + +//------------------------------------------------------------------------------ +// CONNECTION SERVERS MANAGER +//------------------------------------------------------------------------------ +class TServersManager +{ +private: + PConnectionServer Servers[MaxAdapters]; + TSnapCriticalSection *cs; + void Lock(); + void Unlock(); + int CreateServer(longword BindAddress, PConnectionServer &Server); + void AddServer(PConnectionServer Server); +public: + int ServersCount; + TServersManager(); + ~TServersManager(); + + int GetServer(longword BindAddress, PConnectionServer &Server); + void RemovePartner(PConnectionServer Server, PSnap7Partner Partner); + +}; +typedef TServersManager *PServersManager; + +//------------------------------------------------------------------------------ +// CONNECTION SERVER (Don't inherit from TcpSrv to avoid dependence) +//------------------------------------------------------------------------------ + +class TConnListenerThread : public TSnapThread +{ +private: + TMsgSocket *FListener; + TConnectionServer *FServer; +public: + TConnListenerThread(TMsgSocket *Listener, TConnectionServer *Server) + { + FServer=Server; + FListener=Listener; + FreeOnTerminate=false; + }; + void Execute(); +}; +typedef TConnListenerThread *PConnListenerThread; + + +class TConnectionServer +{ +private: + TSnapCriticalSection *cs; + bool FRunning; + // Bind Address + char FLocalAddress[16]; + // Server listener + PConnListenerThread ServerThread; + // Socket listener + PMsgSocket SockListener; + // Finds a partner bound to the address + PSnap7Partner FindPartner(longword Address); + // Locks the Partner list + void Lock(); + // Unlocks the Partner list + void Unlock(); +protected: + // Workers list + PSnap7Partner Partners[MaxPartners]; + bool Destroying; + void Incoming(socket_t Sock); + int Start(); + int FirstFree(); +public: + int PartnersCount; + longword LocalBind; + TConnectionServer(); + ~TConnectionServer(); + + int StartTo(const char *Address); + void Stop(); + int RegisterPartner(PSnap7Partner Partner); + void RemovePartner(PSnap7Partner Partner); + friend class TConnListenerThread; +}; +typedef TConnectionServer * PConnectionServer; + +//------------------------------------------------------------------------------ +// PARTNER THREAD +//------------------------------------------------------------------------------ +class TPartnerThread : public TSnapThread +{ +private: + TSnap7Partner *FPartner; + longword FRecoveryTime; + longword FKaElapsed; +protected: + void Execute(); +public: + TPartnerThread(TSnap7Partner *Partner, longword RecoveryTime) + { + FPartner = Partner; + FRecoveryTime =RecoveryTime; + FreeOnTerminate =false; + }; + ~TPartnerThread(){}; +}; +typedef TPartnerThread *PPartnerThread; +//------------------------------------------------------------------------------ +// S7 PARTNER +//------------------------------------------------------------------------------ + +typedef struct{ + bool First; + bool Done; + uintptr_t Offset; + longword TotalLength; + longword In_R_ID; + longword Elapsed; + byte Seq_Out; +}TRecvStatus; + +typedef struct{ + bool Done; + int Size; + int Result; + longword R_ID; + longword Count; +}TRecvLast; + +extern "C" { +typedef void (S7API *pfn_ParBRecvCallBack)(void * usrPtr, int opResult, longword R_ID, void *pdata, int Size); +typedef void (S7API *pfn_ParBSendCompletion)(void * usrPtr, int opResult); +} + +class TSnap7Partner : public TSnap7Peer +{ +private: + PS7ReqHeader PDUH_in; + void *FRecvUsrPtr; + void *FSendUsrPtr; + PSnapEvent SendEvt; + PSnapEvent RecvEvt; + PConnectionServer FServer; + PPartnerThread FWorkerThread; + bool FSendPending; + bool FRecvPending; + TRecvStatus FRecvStatus; + TRecvLast FRecvLast; + TPendingBuffer TxBuffer; + TPendingBuffer RxBuffer; + longword FSendElapsed; + bool BindError; + byte NextByte; + pfn_ParBRecvCallBack OnBRecv; + pfn_ParBSendCompletion OnBSend; + void ClearRecv(); + byte GetNextByte(); + void CloseWorker(); + bool BlockSend(); + bool PickData(); + bool BlockRecv(); + bool ConnectionConfirm(); +protected: + bool Stopping; + bool Execute(); + void Disconnect(); + bool ConnectToPeer(); + bool PerformFunctionNegotiate(); +public: + bool Active; + bool Running; + longword PeerAddress; + longword SrcAddress; + int BRecvTimeout; + int BSendTimeout; + longword SendTime; + longword RecvTime; + longword RecoveryTime; + longword KeepAliveTime; + longword BytesSent; + longword BytesRecv; + longword SendErrors; + longword RecvErrors; + // The partner is linked when the init sequence is terminated + //(TCP connection + ISO connection + PDU Length negotiation) + bool Linked; + TSnap7Partner(bool CreateActive); + ~TSnap7Partner(); + // Control + int Start(); + int StartTo(const char *LocAddress, const char *RemAddress, word LocTsap, word RemTsap); + int Stop(); + int Status(); + int GetParam(int ParamNumber, void * pValue); + int SetParam(int ParamNumber, void * pValue); + // Block send + int BSend(longword R_ID, void *pUsrData, int Size); + int AsBSend(longword R_ID, void *pUsrData, int Size); + bool CheckAsBSendCompletion(int &opResult); + int WaitAsBSendCompletion(longword Timeout); + int SetSendCallback(pfn_ParBSendCompletion pCompletion, void *usrPtr); + // Block recv + int BRecv(longword &R_ID, void *pData, int &Size, longword Timeout); + bool CheckAsBRecvCompletion(int &opResult, longword &R_ID, + void *pData, int &Size); + int SetRecvCallback(pfn_ParBRecvCallBack pCompletion, void *usrPtr); + + friend class TConnectionServer; + friend class TPartnerThread; +}; + + +#endif // s7_partner_h diff --git a/utils/snap7_src/src/core/s7_peer.cpp b/utils/snap7_src/src/core/s7_peer.cpp new file mode 100644 index 0000000..3e51f90 --- /dev/null +++ b/utils/snap7_src/src/core/s7_peer.cpp @@ -0,0 +1,122 @@ +/*=============================================================================| +| 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_peer.h" +//--------------------------------------------------------------------------- + +TSnap7Peer::TSnap7Peer() +{ + PDUH_out=PS7ReqHeader(&PDU.Payload); + PDURequest=480; // Our request, FPDULength will contain the CPU answer + LastError=0; + cntword = 0; + Destroying = false; +} +//--------------------------------------------------------------------------- +TSnap7Peer::~TSnap7Peer() +{ + Destroying = true; +} +//--------------------------------------------------------------------------- +int TSnap7Peer::SetError(int Error) +{ + if (Error==0) + ClrError(); + else + LastError=Error | LastIsoError | LastTcpError; + return Error; +} +//--------------------------------------------------------------------------- +void TSnap7Peer::ClrError() +{ + LastError=0; + LastIsoError=0; + LastTcpError=0; +} +//--------------------------------------------------------------------------- +word TSnap7Peer::GetNextWord() +{ + if (cntword==0xFFFF) + cntword=0; + return cntword++; +} +//--------------------------------------------------------------------------- +int TSnap7Peer::NegotiatePDULength( ) +{ + int Result, IsoSize = 0; + PReqFunNegotiateParams ReqNegotiate; + PResFunNegotiateParams ResNegotiate; + PS7ResHeader23 Answer; + ClrError(); + // Setup Pointers + ReqNegotiate = PReqFunNegotiateParams(pbyte(PDUH_out) + sizeof(TS7ReqHeader)); + // Header + PDUH_out->P = 0x32; // Always $32 + PDUH_out->PDUType = PduType_request; // $01 + PDUH_out->AB_EX = 0x0000; // Always $0000 + PDUH_out->Sequence = GetNextWord(); // AutoInc + PDUH_out->ParLen = SwapWord(sizeof(TReqFunNegotiateParams)); // 8 bytes + PDUH_out->DataLen = 0x0000; + // Params + ReqNegotiate->FunNegotiate = pduNegotiate; + ReqNegotiate->Unknown = 0x00; + ReqNegotiate->ParallelJobs_1 = 0x0100; + ReqNegotiate->ParallelJobs_2 = 0x0100; + ReqNegotiate->PDULength = SwapWord(PDURequest); + IsoSize = sizeof( TS7ReqHeader ) + sizeof( TReqFunNegotiateParams ); + Result = isoExchangeBuffer(NULL, IsoSize); + if ((Result == 0) && (IsoSize == int(sizeof(TS7ResHeader23) + sizeof(TResFunNegotiateParams)))) + { + // Setup pointers + Answer = PS7ResHeader23(&PDU.Payload); + ResNegotiate = PResFunNegotiateParams(pbyte(Answer) + sizeof(TS7ResHeader23)); + if ( Answer->Error != 0 ) + Result = SetError(errNegotiatingPDU); + if ( Result == 0 ) + PDULength = SwapWord(ResNegotiate->PDULength); + } + return Result; +} +//--------------------------------------------------------------------------- +void TSnap7Peer::PeerDisconnect( ) +{ + ClrError(); + isoDisconnect(true); +} +//--------------------------------------------------------------------------- +int TSnap7Peer::PeerConnect( ) +{ + int Result; + + ClrError(); + Result = isoConnect(); + if (Result == 0) + { + Result = NegotiatePDULength(); + if (Result != 0) + PeerDisconnect(); + } + return Result; +} diff --git a/utils/snap7_src/src/core/s7_peer.h b/utils/snap7_src/src/core/s7_peer.h new file mode 100644 index 0000000..b86f6c3 --- /dev/null +++ b/utils/snap7_src/src/core/s7_peer.h @@ -0,0 +1,58 @@ +/*=============================================================================| +| 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/ | +|=============================================================================*/ +#ifndef s7_peer_h +#define s7_peer_h +//--------------------------------------------------------------------------- +#include "s7_types.h" +#include "s7_isotcp.h" +//--------------------------------------------------------------------------- + +const longword errPeerMask = 0xFFF00000; +const longword errPeerBase = 0x000FFFFF; +const longword errNegotiatingPDU = 0x00100000; + +class TSnap7Peer: public TIsoTcpSocket +{ +private: + word cntword; +protected: + bool Destroying; + PS7ReqHeader PDUH_out; + word GetNextWord(); + int SetError(int Error); + int NegotiatePDULength(); + void ClrError(); +public: + int LastError; + int PDULength; + int PDURequest; + TSnap7Peer(); + ~TSnap7Peer(); + void PeerDisconnect(); + int PeerConnect(); +}; +//--------------------------------------------------------------------------- +#endif diff --git a/utils/snap7_src/src/core/s7_server.cpp b/utils/snap7_src/src/core/s7_server.cpp new file mode 100644 index 0000000..e45e407 --- /dev/null +++ b/utils/snap7_src/src/core/s7_server.cpp @@ -0,0 +1,2158 @@ +/*=============================================================================| +| 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,&SZLSysState,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; +} + + diff --git a/utils/snap7_src/src/core/s7_server.h b/utils/snap7_src/src/core/s7_server.h new file mode 100644 index 0000000..9a845b1 --- /dev/null +++ b/utils/snap7_src/src/core/s7_server.h @@ -0,0 +1,261 @@ +/*=============================================================================| +| 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/ | +|=============================================================================*/ +#ifndef s7_server_h +#define s7_server_h +//--------------------------------------------------------------------------- +#include "snap_tcpsrvr.h" +#include "s7_types.h" +#include "s7_isotcp.h" +//--------------------------------------------------------------------------- + +// Maximum number of DB, change it to increase/decrease the limit. +// The DB table size is 12*MaxDB bytes + +#define MaxDB 2048 // Like a S7 318 +#define MinPduSize 240 +#define CPU315PduSize 240 +//--------------------------------------------------------------------------- +// Server Interface errors +const longword errSrvDBNullPointer = 0x00200000; // Pssed null as PData +const longword errSrvAreaAlreadyExists = 0x00300000; // Area Re-registration +const longword errSrvUnknownArea = 0x00400000; // Unknown area +const longword errSrvInvalidParams = 0x00500000; // Invalid param(s) supplied +const longword errSrvTooManyDB = 0x00600000; // Cannot register DB +const longword errSrvInvalidParamNumber = 0x00700000; // Invalid param (srv_get/set_param) +const longword errSrvCannotChangeParam = 0x00800000; // Cannot change because running + +// Server Area ID (use with Register/unregister - Lock/unlock Area) +const int srvAreaPE = 0; +const int srvAreaPA = 1; +const int srvAreaMK = 2; +const int srvAreaCT = 3; +const int srvAreaTM = 4; +const int srvAreaDB = 5; + +typedef struct{ + word Number; // Number (only for DB) + word Size; // Area size (in bytes) + pbyte PData; // Pointer to area + PSnapCriticalSection cs; +}TS7Area, *PS7Area; + +//------------------------------------------------------------------------------ +// ISOTCP WORKER CLASS +//------------------------------------------------------------------------------ +class TIsoTcpWorker : public TIsoTcpSocket +{ +protected: + virtual bool IsoPerformCommand(int &Size); + virtual bool ExecuteSend(); + virtual bool ExecuteRecv(); +public: + TIsoTcpWorker(){}; + ~TIsoTcpWorker(){}; + // Worker execution + bool Execute(); +}; +//------------------------------------------------------------------------------ +// S7 WORKER CLASS +//------------------------------------------------------------------------------ + +// SZL frame +typedef struct{ + TS7Answer17 Answer; + PReqFunReadSZLFirst ReqParams; + PS7ReqSZLData ReqData; + PS7ResParams7 ResParams; + pbyte ResData; + int ID; + int Index; + bool SZLDone; +}TSZL; + +// Current Event Info +typedef struct{ + word EvRetCode; + word EvArea; + word EvIndex; + word EvStart; + word EvSize; +}TEv; + +// Current Block info +typedef struct{ + PReqFunGetBlockInfo ReqParams; + PResFunGetBlockInfo ResParams; + TS7Answer17 Answer; + word evError; + word DataLength; +}TCB; + +class TSnap7Server; // forward declaration + +class TS7Worker : public TIsoTcpWorker +{ +private: + PS7ReqHeader PDUH_in; + int DBCnt; + byte LastBlk; + TSZL SZL; + byte BCD(word Value); + // Checks the consistence of the incoming PDU + bool CheckPDU_in(int PayloadSize); + void FillTime(PS7Time PTime); +protected: + int DataSizeByte(int WordLength); + bool ExecuteRecv(); + void DoEvent(longword Code, word RetCode, word Param1, word Param2, + word Param3, word Param4); + void DoReadEvent(longword Code, word RetCode, word Param1, word Param2, + word Param3, word Param4); + void FragmentSkipped(int Size); + // Entry parse + bool IsoPerformCommand(int &Size); + // First stage parse + bool PerformPDUAck(int &Size); + bool PerformPDURequest(int &Size); + bool PerformPDUUsrData(int &Size); + // Second stage parse : PDU Request + PS7Area GetArea(byte S7Code, word index); + // Group Read Area + bool PerformFunctionRead(); + // Subfunctions Read Data + word ReadArea(PResFunReadItem ResItemData, PReqFunReadItem ReqItemPar, + int &PDURemainder,TEv &EV); + word RA_NotFound(PResFunReadItem ResItem, TEv &EV); + word RA_OutOfRange(PResFunReadItem ResItem, TEv &EV); + word RA_SizeOverPDU(PResFunReadItem ResItem, TEv &EV); + // Group Write Area + bool PerformFunctionWrite(); + // Subfunctions Write Data + byte WriteArea(PReqFunWriteDataItem ReqItemData, PReqFunWriteItem ReqItemPar, + TEv &EV); + byte WA_NotFound(TEv &EV); + byte WA_InvalidTransportSize(TEv &EV); + byte WA_OutOfRange(TEv &EV); + byte WA_DataSizeMismatch(TEv &EV); + // Negotiate PDU Length + bool PerformFunctionNegotiate(); + // Control + bool PerformFunctionControl(byte PduFun); + // Up/Download + bool PerformFunctionUpload(); + bool PerformFunctionDownload(); + // Second stage parse : PDU User data + bool PerformGroupProgrammer(); + bool PerformGroupCyclicData(); + bool PerformGroupSecurity(); + // Group Block(s) Info + bool PerformGroupBlockInfo(); + // Subfunctions Block info + void BLK_ListAll(TCB &CB); + void BLK_ListBoT(byte BlockType, bool Start, TCB &CB); + void BLK_NoResource_ListBoT(PDataFunGetBot Data, TCB &CB); + void BLK_GetBlkInfo(TCB &CB); + void BLK_NoResource_GetBlkInfo(PResDataBlockInfo Data, TCB &CB); + void BLK_GetBlockNum_GetBlkInfo(int &BlkNum, PReqDataBlockInfo ReqData); + void BLK_DoBlockInfo_GetBlkInfo(PS7Area DB, PResDataBlockInfo Data, TCB &CB); + // Clock Group + bool PerformGetClock(); + bool PerformSetClock(); + // SZL Group + bool PerformGroupSZL(); + // Subfunctions (called by PerformGroupSZL) + void SZLNotAvailable(); + void SZLSystemState(); + void SZLData(void *P, int len); + void SZL_ID424(); + void SZL_ID131_IDX003(); +public: + TSnap7Server *FServer; + int FPDULength; + TS7Worker(); + ~TS7Worker(){}; +}; + +typedef TS7Worker *PS7Worker; +//------------------------------------------------------------------------------ +// S7 SERVER CLASS +//------------------------------------------------------------------------------ +extern "C" +{ + typedef int (S7API *pfn_RWAreaCallBack)(void *usrPtr, int Sender, int Operation, PS7Tag PTag, void *pUsrData); +} +const int OperationRead = 0; +const int OperationWrite = 1; + +class TSnap7Server : public TCustomMsgServer +{ +private: + // Read Callback related + pfn_SrvCallBack OnReadEvent; + pfn_RWAreaCallBack OnRWArea; + // Critical section to lock Read/Write Hook Area + PSnapCriticalSection CSRWHook; + void *FReadUsrPtr; + void *FRWAreaUsrPtr; + void DisposeAll(); + int FindFirstFreeDB(); + int IndexOfDB(word DBNumber); +protected: + int DBCount; + int DBLimit; + PS7Area DB[MaxDB]; // DB + PS7Area HA[5]; // MK,PE,PA,TM,CT + PS7Area FindDB(word DBNumber); + PWorkerSocket CreateWorkerSocket(socket_t Sock); + bool ResourceLess; + word ForcePDU; + int RegisterDB(word Number, void *pUsrData, word Size); + int RegisterSys(int AreaCode, void *pUsrData, word Size); + int UnregisterDB(word DBNumber); + int UnregisterSys(int AreaCode); + // The Read event + void DoReadEvent(int Sender, longword Code, word RetCode, word Param1, + word Param2, word Param3, word Param4); + bool DoReadArea(int Sender, int Area, int DBNumber, int Start, int Size, int WordLen, void *pUsrData); + bool DoWriteArea(int Sender, int Area, int DBNumber, int Start, int Size, int WordLen, void *pUsrData); +public: + int WorkInterval; + byte CpuStatus; + TSnap7Server(); + ~TSnap7Server(); + int StartTo(const char *Address); + int GetParam(int ParamNumber, void *pValue); + int SetParam(int ParamNumber, void *pValue); + int RegisterArea(int AreaCode, word Index, void *pUsrData, word Size); + int UnregisterArea(int AreaCode, word Index); + int LockArea(int AreaCode, word DBNumber); + int UnlockArea(int AreaCode, word DBNumber); + // Sets Event callback + int SetReadEventsCallBack(pfn_SrvCallBack PCallBack, void *UsrPtr); + int SetRWAreaCallBack(pfn_RWAreaCallBack PCallBack, void *UsrPtr); + friend class TS7Worker; +}; +typedef TSnap7Server *PSnap7Server; + +#endif // s7_server_h + diff --git a/utils/snap7_src/src/core/s7_text.cpp b/utils/snap7_src/src/core/s7_text.cpp new file mode 100644 index 0000000..8f54382 --- /dev/null +++ b/utils/snap7_src/src/core/s7_text.cpp @@ -0,0 +1,788 @@ +/*=============================================================================| +| 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_text.h" +//--------------------------------------------------------------------------- +#ifndef OS_WINDOWS +static char* itoa(int value, char* result, int base) { + // check that the base if valid + if (base < 2 || base > 36){ + *result = '\0'; return result; + + } + char* ptr = result, *ptr1 = result, tmp_char; + int tmp_value; + + do { + tmp_value = value; + value /= base; + *ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz" [35 + (tmp_value - value * base)]; + } while ( value ); + + // Apply negative sign + if (tmp_value < 0) *ptr++ = '-'; + *ptr-- = '\0'; + while(ptr1 < ptr) { + tmp_char = *ptr; + *ptr--= *ptr1; + *ptr1++ = tmp_char; + } + return result; +} +#endif +//--------------------------------------------------------------------------- +char* NumToString(int Value, int Base, int Len, char* Result) +{ + char CNumber[64]; + char Pad[65] = "0000000000000000000000000000000000000000000000000000000000000000"; + itoa(Value, CNumber, Base); + + if (Len > 0) + { + int Delta = Len - strlen(CNumber); // Len is max 8 in this program + if (Delta > 0) + { + strncpy(Result, Pad, Delta); + Result[Delta] = '\0'; + strcat(Result, CNumber); + } + else + strcpy(Result, CNumber); + } + else + strcpy(Result, CNumber); + + return Result; +} +//--------------------------------------------------------------------------- +char* IntToString(int Value, char* Result) +{ + return NumToString(Value, 10, 0, Result); +} +//--------------------------------------------------------------------------- +char* TimeToString(time_t dt, char* Result) +{ + struct tm * DateTime = localtime(&dt); + if (DateTime != NULL) + strftime(Result, 50, "%Y-%m-%d %H:%M:%S", DateTime); + else + *Result = '\0'; + return Result; +} + +//--------------------------------------------------------------------------- +char* IpAddressToString(int IP, char* Result) +{ + in_addr Addr; + Addr.s_addr = IP; + strcpy(Result, inet_ntoa(Addr)); + return Result; +} +//--------------------------------------------------------------------------- +#define WSAEINVALIDADDRESS 12001 + +char* TcpTextOf(int Error, char* Result) +{ + switch (Error) + { + case 0: *Result='\0';break; + case WSAEINTR: strcpy(Result," TCP : Interrupted system call\0");break; + case WSAEBADF: strcpy(Result," TCP : Bad file number\0");break; + case WSAEACCES: strcpy(Result," TCP : Permission denied\0");break; + case WSAEFAULT: strcpy(Result," TCP : Bad address\0");break; + case WSAEINVAL: strcpy(Result," TCP : Invalid argument\0");break; + case WSAEMFILE: strcpy(Result," TCP : Too many open files\0");break; + case WSAEWOULDBLOCK: strcpy(Result," TCP : Operation would block\0");break; + case WSAEINPROGRESS: strcpy(Result," TCP : Operation now in progress\0");break; + case WSAEALREADY: strcpy(Result," TCP : Operation already in progress\0");break; + case WSAENOTSOCK: strcpy(Result," TCP : Socket operation on non socket\0");break; + case WSAEDESTADDRREQ: strcpy(Result," TCP : Destination address required\0");break; + case WSAEMSGSIZE: strcpy(Result," TCP : Message too long\0");break; + case WSAEPROTOTYPE: strcpy(Result," TCP : Protocol wrong type for Socket\0");break; + case WSAENOPROTOOPT: strcpy(Result," TCP : Protocol not available\0");break; + case WSAEPROTONOSUPPORT: strcpy(Result," TCP : Protocol not supported\0");break; + case WSAESOCKTNOSUPPORT: strcpy(Result," TCP : Socket not supported\0");break; + case WSAEOPNOTSUPP: strcpy(Result," TCP : Operation not supported on Socket\0");break; + case WSAEPFNOSUPPORT: strcpy(Result," TCP : Protocol family not supported\0");break; + case WSAEAFNOSUPPORT: strcpy(Result," TCP : Address family not supported\0");break; + case WSAEADDRINUSE: strcpy(Result," TCP : Address already in use\0");break; + case WSAEADDRNOTAVAIL: strcpy(Result," TCP : Can't assign requested address\0");break; + case WSAENETDOWN: strcpy(Result," TCP : Network is down\0");break; + case WSAENETUNREACH: strcpy(Result," TCP : Network is unreachable\0");break; + case WSAENETRESET: strcpy(Result," TCP : Network dropped connection on reset\0");break; + case WSAECONNABORTED: strcpy(Result," TCP : Software caused connection abort\0");break; + case WSAECONNRESET: strcpy(Result," TCP : Connection reset by peer\0");break; + case WSAENOBUFS: strcpy(Result," TCP : No Buffer space available\0");break; + case WSAEISCONN: strcpy(Result," TCP : Socket is already connected\0");break; + case WSAENOTCONN: strcpy(Result," TCP : Socket is not connected\0");break; + case WSAESHUTDOWN: strcpy(Result," TCP : Can't send after Socket shutdown\0");break; + case WSAETOOMANYREFS: strcpy(Result," TCP : Too many references:can't splice\0");break; + case WSAETIMEDOUT: strcpy(Result," TCP : Connection timed out\0");break; + case WSAECONNREFUSED: strcpy(Result," TCP : Connection refused\0");break; + case WSAELOOP: strcpy(Result," TCP : Too many levels of symbolic links\0");break; + case WSAENAMETOOLONG: strcpy(Result," TCP : File name is too long\0");break; + case WSAEHOSTDOWN: strcpy(Result," TCP : Host is down\0");break; + case WSAEHOSTUNREACH: strcpy(Result," TCP : Unreachable peer\0");break; + case WSAENOTEMPTY: strcpy(Result," TCP : Directory is not empty\0");break; + case WSAEUSERS: strcpy(Result," TCP : Too many users\0");break; + case WSAEDQUOT: strcpy(Result," TCP : Disk quota exceeded\0");break; + case WSAESTALE: strcpy(Result," TCP : Stale NFS file handle\0");break; + case WSAEREMOTE: strcpy(Result," TCP : Too many levels of remote in path\0");break; + #ifdef OS_WINDOWS + case WSAEPROCLIM: strcpy(Result," TCP : Too many processes\0");break; + case WSASYSNOTREADY: strcpy(Result," TCP : Network subsystem is unusable\0");break; + case WSAVERNOTSUPPORTED: strcpy(Result," TCP : Winsock DLL cannot support this application\0");break; + case WSANOTINITIALISED: strcpy(Result," TCP : Winsock not initialized\0");break; + case WSAEDISCON: strcpy(Result," TCP : Disconnect\0");break; + case WSAHOST_NOT_FOUND: strcpy(Result," TCP : Host not found\0");break; + case WSATRY_AGAIN: strcpy(Result," TCP : Non authoritative - host not found\0");break; + case WSANO_RECOVERY: strcpy(Result," TCP : Non recoverable error\0");break; + case WSANO_DATA: strcpy(Result," TCP : Valid name, no data record of requested type\0");break; + #endif + case WSAEINVALIDADDRESS: strcpy(Result," TCP : Invalid address\0");break; + default: + { + char CNumber[16]; + strcpy(Result, " TCP : Other Socket error ("); + strcat(Result, IntToString(Error, CNumber)); + strcat(Result, ")"); + break; + } + } + return Result; +} +//--------------------------------------------------------------------------- +char* IsoTextOf(int Error, char* Result) +{ + switch (Error) + { + case 0 : *Result='\0';break; + case errIsoConnect: strcpy(Result," ISO : Connection error\0");break; + case errIsoDisconnect: strcpy(Result," ISO : Disconnect error\0");break; + case errIsoInvalidPDU: strcpy(Result," ISO : Bad PDU format\0");break; + case errIsoInvalidDataSize: strcpy(Result," ISO : Datasize passed to send/recv buffer is invalid\0");break; + case errIsoNullPointer: strcpy(Result," ISO : Null passed as pointer\0");break; + case errIsoShortPacket: strcpy(Result," ISO : A short packet received\0");break; + case errIsoTooManyFragments: strcpy(Result," ISO : Too many packets without EoT flag\0");break; + case errIsoPduOverflow: strcpy(Result," ISO : The sum of fragments data exceded maximum packet size\0");break; + case errIsoSendPacket: strcpy(Result," ISO : An error occurred during send\0");break; + case errIsoRecvPacket: strcpy(Result," ISO : An error occurred during recv\0");break; + case errIsoInvalidParams: strcpy(Result," ISO : Invalid connection params (wrong TSAPs)\0");break; + default: + { + char CNumber[16]; + strcpy(Result, " ISO : Unknown error (0x"); + strcat(Result, NumToString(Error, 16, 8, CNumber)); + strcat(Result, ")"); + break; + } + } + return Result; +} +//--------------------------------------------------------------------------- +char* CliTextOf(int Error, char* Result) +{ + switch (Error) + { + case 0 : *Result='\0';break; + case errNegotiatingPDU : strcpy(Result,"CPU : Error in PDU negotiation\0");break; + case errCliInvalidParams : strcpy(Result,"CLI : invalid param(s) supplied\0");break; + case errCliJobPending : strcpy(Result,"CLI : Job pending\0");break; + case errCliTooManyItems : strcpy(Result,"CLI : too may items (>20) in multi read/write\0");break; + case errCliInvalidWordLen : strcpy(Result,"CLI : invalid WordLength\0");break; + case errCliPartialDataWritten : strcpy(Result,"CLI : Partial data written\0");break; + case errCliSizeOverPDU : strcpy(Result,"CPU : total data exceeds the PDU size\0");break; + case errCliInvalidPlcAnswer : strcpy(Result,"CLI : invalid CPU answer\0");break; + case errCliAddressOutOfRange : strcpy(Result,"CPU : Address out of range\0");break; + case errCliInvalidTransportSize : strcpy(Result,"CPU : Invalid Transport size\0");break; + case errCliWriteDataSizeMismatch : strcpy(Result,"CPU : Data size mismatch\0");break; + case errCliItemNotAvailable : strcpy(Result,"CPU : Item not available\0");break; + case errCliInvalidValue : strcpy(Result,"CPU : Invalid value supplied\0");break; + case errCliCannotStartPLC : strcpy(Result,"CPU : Cannot start PLC\0");break; + case errCliAlreadyRun : strcpy(Result,"CPU : PLC already RUN\0");break; + case errCliCannotStopPLC : strcpy(Result,"CPU : Cannot stop PLC\0");break; + case errCliCannotCopyRamToRom : strcpy(Result,"CPU : Cannot copy RAM to ROM\0");break; + case errCliCannotCompress : strcpy(Result,"CPU : Cannot compress\0");break; + case errCliAlreadyStop : strcpy(Result,"CPU : PLC already STOP\0");break; + case errCliFunNotAvailable : strcpy(Result,"CPU : Function not available\0");break; + case errCliUploadSequenceFailed : strcpy(Result,"CPU : Upload sequence failed\0");break; + case errCliInvalidDataSizeRecvd : strcpy(Result,"CLI : Invalid data size received\0");break; + case errCliInvalidBlockType : strcpy(Result,"CLI : Invalid block type\0");break; + case errCliInvalidBlockNumber : strcpy(Result,"CLI : Invalid block number\0");break; + case errCliInvalidBlockSize : strcpy(Result,"CLI : Invalid block size\0");break; + case errCliDownloadSequenceFailed : strcpy(Result,"CPU : Download sequence failed\0");break; + case errCliInsertRefused : strcpy(Result,"CPU : block insert refused\0");break; + case errCliDeleteRefused : strcpy(Result,"CPU : block delete refused\0");break; + case errCliNeedPassword : strcpy(Result,"CPU : Function not authorized for current protection level\0");break; + case errCliInvalidPassword : strcpy(Result,"CPU : Invalid password\0");break; + case errCliNoPasswordToSetOrClear : strcpy(Result,"CPU : No password to set or clear\0");break; + case errCliJobTimeout : strcpy(Result,"CLI : Job Timeout\0");break; + case errCliFunctionRefused : strcpy(Result,"CLI : function refused by CPU (Unknown error)\0");break; + case errCliPartialDataRead : strcpy(Result,"CLI : Partial data read\0");break; + case errCliBufferTooSmall : strcpy(Result,"CLI : The buffer supplied is too small to accomplish the operation\0");break; + case errCliDestroying : strcpy(Result,"CLI : Cannot perform (destroying)\0");break; + case errCliInvalidParamNumber : strcpy(Result,"CLI : Invalid Param Number\0");break; + case errCliCannotChangeParam : strcpy(Result,"CLI : Cannot change this param now\0");break; + default : + { + char CNumber[16]; + strcpy(Result, "CLI : Unknown error (0x"); + strcat(Result, NumToString(Error, 16, 8, CNumber)); + strcat(Result, ")"); + break; + } + }; + return Result; +} +//--------------------------------------------------------------------------- +char* SrvTextOf(int Error, char* Result) +{ + switch (Error) + { + case 0: *Result = '\0'; break; + case errSrvCannotStart: strcpy(Result, "SRV : Server cannot start\0"); break; + case errSrvDBNullPointer: strcpy(Result, "SRV : Null passed as area pointer\0"); break; + case errSrvAreaAlreadyExists: strcpy(Result, "SRV : Cannot register area since already exists\0"); break; + case errSrvUnknownArea: strcpy(Result, "SRV : Unknown Area code\0"); break; + case errSrvInvalidParams: strcpy(Result, "SRV : Invalid param(s) supplied\0"); break; + case errSrvTooManyDB: strcpy(Result, "SRV : DB Limit reached\0"); break; + case errSrvInvalidParamNumber: strcpy(Result, "SRV : Invalid Param Number\0"); break; + case errSrvCannotChangeParam: strcpy(Result, "SRV : Cannot change this param now\0");break; + default: + { + char CNumber[16]; + strcpy(Result, "SRV : Unknown error (0x"); + strcat(Result, NumToString(Error, 16, 8, CNumber)); + strcat(Result, ")"); + break; + } + }; + return Result; +} +//--------------------------------------------------------------------------- +char* ParTextOf(int Error, char* Result) +{ + switch(Error) + { + case 0: *Result = '\0'; break; + case errParAddressInUse : strcpy(Result, "PAR : Local address already in use");break; + case errParNoRoom : strcpy(Result, "PAR : No more partners available");break; + case errServerNoRoom : strcpy(Result, "PAR : No more servers available");break; + case errParInvalidParams : strcpy(Result, "PAR : Invalid parameter supplied");break; + case errParNotLinked : strcpy(Result, "PAR : Cannot perform, Partner not linked");break; + case errParBusy : strcpy(Result, "PAR : Cannot perform, Partner Busy");break; + case errParFrameTimeout : strcpy(Result, "PAR : Frame timeout");break; + case errParInvalidPDU : strcpy(Result, "PAR : Invalid PDU received");break; + case errParSendTimeout : strcpy(Result, "PAR : Send timeout");break; + case errParRecvTimeout : strcpy(Result, "PAR : Recv timeout");break; + case errParSendRefused : strcpy(Result, "PAR : Send refused by peer");break; + case errParNegotiatingPDU : strcpy(Result, "PAR : Error negotiating PDU");break; + case errParSendingBlock : strcpy(Result, "PAR : Error Sending Block");break; + case errParRecvingBlock : strcpy(Result, "PAR : Error Receiving Block");break; + case errParBindError : strcpy(Result, "PAR : Error Binding");break; + case errParDestroying : strcpy(Result, "PAR : Cannot perform (destroying)");break; + case errParInvalidParamNumber: strcpy(Result, "PAR : Invalid Param Number");break; + case errParCannotChangeParam : strcpy(Result, "PAR : Cannot change this param now");break; + case errParBufferTooSmall : strcpy(Result, "PAR : The buffer supplied is too small to accomplish the operation");break; + default: + { + char CNumber[16]; + strcpy(Result, "PAR : Unknown error (0x"); + strcat(Result, NumToString(Error, 16, 8, CNumber)); + strcat(Result, ")"); + break; + } + } + return Result; +} +//--------------------------------------------------------------------------- +char* ErrCliText(int Error, char * Result, int TextLen) +{ + char TcpError[128]; + char IsoError[128]; + char CliError[256]; + if (Error != 0) + { + switch (Error) + { + case errLibInvalidParam : strncpy(Result,"LIB : Invalid param supplied\0",TextLen);break; + case errLibInvalidObject: strncpy(Result, "LIB : Invalid object supplied\0", TextLen); break; + default : + { + CliTextOf(Error & ErrS7Mask, CliError); + strcat(CliError, IsoTextOf(Error & ErrIsoMask, IsoError)); + strcat(CliError, TcpTextOf(Error & ErrTcpMask, TcpError)); + strncpy(Result, CliError, TextLen); + } + } + } + else + strncpy(Result, "OK\0", TextLen); + return Result; +} +//--------------------------------------------------------------------------- +char* ErrSrvText(int Error, char* Result, int TextLen) +{ + char TcpError[128]; + char IsoError[128]; + char SrvError[256]; + if (Error != 0) + { + switch (Error) + { + case errLibInvalidParam: strncpy(Result, "LIB : Invalid param supplied\0", TextLen); break; + case errLibInvalidObject: strncpy(Result, "LIB : Invalid object supplied\0", TextLen); break; + default: + { + SrvTextOf(Error & ErrS7Mask, SrvError); + strcat(SrvError, IsoTextOf(Error & ErrIsoMask, IsoError)); + strcat(SrvError, TcpTextOf(Error & ErrTcpMask, TcpError)); + strncpy(Result, SrvError, TextLen); + } + } + } + else + strncpy(Result, "OK\0", TextLen); + return Result; +} +//--------------------------------------------------------------------------- +char* ErrParText(int Error, char* Result, int TextLen) +{ + char TcpError[128]; + char IsoError[128]; + char ParError[256]; + if (Error != 0) + { + switch (Error) + { + case errLibInvalidParam: strncpy(Result, "LIB : Invalid param supplied\0", TextLen); break; + case errLibInvalidObject: strncpy(Result, "LIB : Invalid object supplied\0", TextLen); break; + default: + { + ParTextOf(Error & ErrS7Mask, ParError); + strcat(ParError, IsoTextOf(Error & ErrIsoMask, IsoError)); + strcat(ParError, TcpTextOf(Error & ErrTcpMask, TcpError)); + strncpy(Result, ParError, TextLen); + } + } + } + else + strncpy(Result, "OK\0", TextLen); + return Result; +} +//--------------------------------------------------------------------------- +// SERVER EVENTS TEXT +//--------------------------------------------------------------------------- +char* SenderText(TSrvEvent &Event, char* Result) +{ + char Buf[64]; + char Add[16]; + TimeToString(Event.EvtTime, Buf); + if (Event.EvtSender != 0) + { + strcat(Buf, " ["); + strcat(Buf, IpAddressToString(Event.EvtSender, Add)); + strcat(Buf, "] "); + } + else + strcat(Buf, " Server "); + strcpy(Result, Buf); + return Result; +} +//--------------------------------------------------------------------------- +char* TcpServerEventText(TSrvEvent &Event, char* Result) +{ + char S[256]; + char Buf[128]; + + strcpy(S, SenderText(Event, Buf)); + + switch (Event.EvtCode) + { + case evcServerStarted : strcat(S,"started");break; + case evcServerStopped : strcat(S,"stopped");break; + case evcListenerCannotStart: + strcat(S, "Cannot start listener - Socket Error : "); + strcat(S, TcpTextOf(Event.EvtRetCode,Buf)); + break; + case evcClientAdded : strcat(S,"Client added");break; + case evcClientRejected : strcat(S,"Client refused");break; + case evcClientNoRoom : strcat(S,"A client was refused due to maximum connections number");break; + case evcClientException : strcat(S,"Client exception");break; + case evcClientDisconnected : strcat(S,"Client disconnected by peer");break; + case evcClientTerminated : strcat(S,"Client terminated");break; + case evcClientsDropped: + strcat(S, IntToString(Event.EvtParam1, Buf)); + strcat(S, " clients have been dropped bacause unresponsive"); + break; + default: + strcat(S, "Unknown event ("); + strcat(S, IntToString(Event.EvtCode, Buf)); + strcat(S,")"); + break; + }; + strcpy(Result, S); + return Result; +} +//--------------------------------------------------------------------------- +char* PDUText(TSrvEvent &Event, char* Result) +{ + char S[256]; + char Buf[128]; + switch (Event.EvtRetCode) + { + case evrFragmentRejected: + strcpy(S, "Fragment of "); + strcat(S, IntToString(Event.EvtParam1, Buf)); + strcat(S, " bytes rejected"); + break; + case evrMalformedPDU: + strcpy(S, "Malformed PDU of "); + strcat(S, IntToString(Event.EvtParam1, Buf)); + strcat(S, " bytes rejected"); + break; + case evrSparseBytes: + strcpy(S, "Message of sparse "); + strcat(S, IntToString(Event.EvtParam1, Buf)); + strcat(S, " bytes rejected"); + break; + case evrCannotHandlePDU: + strcpy(S, "Cannot handle this PDU"); + break; + case evrNotImplemented: + switch (Event.EvtParam1) + { + case grCyclicData: + strcpy(S, "Function group cyclic data not yet implemented"); + break; + case grProgrammer: + strcpy(S, "Function group programmer not yet implemented"); + break; + } + break; + default: + strcpy(S, "Unknown Return code ("); + strcat(S, IntToString(Event.EvtRetCode, Buf)); + strcat(S, ")"); + break; + } + strcpy(Result, S); + return Result; +} +//--------------------------------------------------------------------------- +char* TxtArea(TSrvEvent &Event, char* Result) +{ + char S[64]; + char Buf[32]; + switch (Event.EvtParam1) + { + case S7AreaPE: strcpy(S, "Area : PE, "); break; + case S7AreaPA: strcpy(S, "Area : PA, "); break; + case S7AreaMK: strcpy(S, "Area : MK, "); break; + case S7AreaCT: strcpy(S, "Area : CT, "); break; + case S7AreaTM: strcpy(S, "Area : TM, "); break; + case S7AreaDB: + strcpy(S, "Area : DB"); + strcat(S, IntToString(Event.EvtParam2, Buf)); + strcat(S,", "); + break; + default: + strcpy(S, "Unknown area ("); + strcat(S, IntToString(Event.EvtParam2, Buf)); + strcat(S,")"); + break; + } + strcpy(Result, S); + return Result; +} +//--------------------------------------------------------------------------- +char* TxtStartSize(TSrvEvent &Event, char* Result) +{ + char N[32]; + strcpy(Result, "Start : "); + strcat(Result, IntToString(Event.EvtParam3, N)); + strcat(Result, ", Size : "); + strcat(Result, IntToString(Event.EvtParam4, N)); + return Result; +} +//--------------------------------------------------------------------------- +char* TxtDataResult(TSrvEvent &Event, char* Result) +{ + char N[32]; + switch (Event.EvtRetCode) + { + case evrNoError: + strcpy(Result," --> OK"); + break; + case evrErrException: + strcpy(Result, " --> Exception error"); + break; + case evrErrAreaNotFound: + strcpy(Result, " --> Area not found"); + break; + case evrErrOutOfRange: + strcpy(Result, " --> Out of range"); + break; + case evrErrOverPDU: + strcpy(Result, " --> Data size exceeds PDU size"); + break; + case evrErrTransportSize: + strcpy(Result, " --> Invalid transport size"); + break; + case evrDataSizeMismatch: + strcpy(Result, " --> Data size mismatch"); + break; + default: + strcpy(Result, " --> Unknown error code ("); + strcat(Result, IntToString(Event.EvtRetCode, N)); + strcat(Result,")"); + break; + }; + return Result; +} +//--------------------------------------------------------------------------- +char* ControlText(word Code, char* Result) +{ + char N[64]; + strcpy(Result, "CPU Control request : "); + switch (Code) + { + case CodeControlUnknown: + strcat(Result,"Unknown"); + break; + case CodeControlColdStart: + strcat(Result, "Cold START --> OK"); + break; + case CodeControlWarmStart: + strcat(Result, "Warm START --> OK"); + break; + case CodeControlStop: + strcat(Result, "STOP --> OK"); + break; + case CodeControlCompress: + strcat(Result, "Memory compress --> OK"); + break; + case CodeControlCpyRamRom: + strcat(Result, "Copy Ram to Rom --> OK"); + break; + case CodeControlInsDel: + strcat(Result, "Block Insert or Delete --> OK"); + break; + default : + strcat(Result, "Unknown control code ("); + strcat(Result, IntToString(Code, N)); + strcat(Result,")"); + } + return Result; +} +//--------------------------------------------------------------------------- +char* ClockText(word Code, char* Result) +{ + if (Code==evsGetClock) + strcpy(Result,"System clock read requested"); + else + strcpy(Result, "System clock write requested"); + return Result; +} +//--------------------------------------------------------------------------- +char* ReadSZLText(TSrvEvent &Event, char* Result) +{ + char S[128]; + char N[64]; + strcpy(S, "Read SZL request, ID:0x"); + strcat(S, NumToString(Event.EvtParam1, 16, 4, N)); + strcat(S, " INDEX:0x"); + strcat(S, NumToString(Event.EvtParam2, 16, 4, N)); + + if (Event.EvtRetCode == evrNoError) + strcat(S, " --> OK"); + else + strcat(S, " --> NOT AVAILABLE"); + strcpy(Result, S); + return Result; +} +//--------------------------------------------------------------------------- +char* UploadText(TSrvEvent &Event, char* Result) +{ + strcpy(Result,"Block upload requested --> NOT PERFORMED (due to invalid security level)"); + return Result; +} +//--------------------------------------------------------------------------- +char* DownloadText(TSrvEvent &Event, char* Result) +{ + strcpy(Result, "Block download requested --> NOT PERFORMED (due to invalid security level)"); + return Result; +} +//--------------------------------------------------------------------------- +char* StrBlockType(word Code, char* Result) +{ + char N[64]; + switch (Code) + { + case Block_OB: + strcpy(Result, "OB"); + break; + case Block_DB: + strcpy(Result, "DB"); + break; + case Block_SDB: + strcpy(Result, "SDB"); + break; + case Block_FC: + strcpy(Result, "FC"); + break; + case Block_SFC: + strcpy(Result, "SFC"); + break; + case Block_FB: + strcpy(Result, "FB"); + break; + case Block_SFB: + strcpy(Result, "SFB"); + break; + default: + strcpy(Result, "[Unknown 0x"); + strcat(Result, NumToString(Code, 16, 4, N)); + strcat(Result,"]"); + break; + }; + return Result; +} +//--------------------------------------------------------------------------- +char* BlockInfoText(TSrvEvent &Event, char* Result) +{ + char S[64]; + switch (Event.EvtParam1) + { + case evsGetBlockList: + strcpy(Result, "Block list requested"); + break; + case evsStartListBoT: + strcpy(Result, "Block of type "); + strcat(Result, StrBlockType(Event.EvtParam2,S)); + strcat(Result, " list requested (start sequence)"); + break; + case evsListBoT: + strcpy(Result, "Block of type "); + strcat(Result, StrBlockType(Event.EvtParam2, S)); + strcat(Result, " list requested (next part)"); + break; + case evsGetBlockInfo: + strcpy(Result, "Block info requested "); + strcat(Result, StrBlockType(Event.EvtParam2, S)); + strcat(Result, " "); + strcat(Result, IntToString(Event.EvtParam3,S)); + break; + }; + if (Event.EvtRetCode == evrNoError) + strcat(Result, " --> OK"); + else + strcat(Result, " --> NOT AVAILABLE"); + return Result; +} +//--------------------------------------------------------------------------- +char* SecurityText(TSrvEvent &Event, char* Result) +{ + switch (Event.EvtParam1) + { + case evsSetPassword: + strcpy(Result,"Security request : Set session password --> OK"); + break; + case evsClrPassword: + strcpy(Result, "Security request : Clear session password --> OK"); + break; + default: + strcpy(Result, "Security request : Unknown Subfunction"); + break; + }; + return Result; +} +//--------------------------------------------------------------------------- +char* EvtSrvText(TSrvEvent &Event, char* Result, int TextLen) +{ + char S[256]; + char C[128]; + + if (Event.EvtCode > evcSnap7Base) + { + strcpy(S, SenderText(Event, C)); + switch (Event.EvtCode) + { + case evcPDUincoming: + strcat(S, "PDU incoming : "); + strcat(S,PDUText(Event,C)); + break; + case evcDataRead: + strcat(S, "Read request, "); + strcat(S, TxtArea(Event, C)); + strcat(S, TxtStartSize(Event, C)); + strcat(S, TxtDataResult(Event, C)); + break; + case evcDataWrite: + strcat(S, "Write request, "); + strcat(S, TxtArea(Event, C)); + strcat(S, TxtStartSize(Event, C)); + strcat(S, TxtDataResult(Event, C)); + break; + case evcNegotiatePDU: + strcat(S, "The client requires a PDU size of "); + strcat(S, IntToString(Event.EvtParam1, C)); + strcat(S," bytes"); + break; + case evcControl: + strcat(S, ControlText(Event.EvtParam1,C)); + break; + case evcReadSZL: + strcat(S, ReadSZLText(Event,C)); + break; + case evcClock: + strcat(S, ClockText(Event.EvtParam1,C)); + break; + case evcUpload: + strcat(S, UploadText(Event,C)); + break; + case evcDownload: + strcat(S, DownloadText(Event,C)); + break; + case evcDirectory: + strcat(S, BlockInfoText(Event,C)); + break; + case evcSecurity: + strcat(S, SecurityText(Event,C)); + break; + default: + strcat(S, "Unknown event ("); + strcat(S, IntToString(Event.EvtCode, C)); + strcat(S,")"); + break; + } + } + else + strcpy(S,TcpServerEventText(Event,C)); + + strncpy(Result, S, TextLen); + return Result; +} + diff --git a/utils/snap7_src/src/core/s7_text.h b/utils/snap7_src/src/core/s7_text.h new file mode 100644 index 0000000..b7018cd --- /dev/null +++ b/utils/snap7_src/src/core/s7_text.h @@ -0,0 +1,49 @@ +/*=============================================================================| +| 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/ | +|=============================================================================*/ +#ifndef s7_text_h +#define s7_text_h +//--------------------------------------------------------------------------- +#include "s7_micro_client.h" +#include "s7_server.h" +#include "s7_partner.h" +//--------------------------------------------------------------------------- + +const int errLibInvalidParam = -1; +const int errLibInvalidObject = -2; +// Errors areas definition +const longword ErrTcpMask = 0x0000FFFF; +const longword ErrIsoMask = 0x000F0000; +const longword ErrS7Mask = 0xFFF00000; + +char* ErrCliText(int Error, char* Result, int TextLen); +char* ErrSrvText(int Error, char* Result, int TextLen); +char* ErrParText(int Error, char* Result, int TextLen); +char* EvtSrvText(TSrvEvent &Event, char* Result, int TextLen); + + +#endif + + diff --git a/utils/snap7_src/src/core/s7_types.h b/utils/snap7_src/src/core/s7_types.h new file mode 100644 index 0000000..5639265 --- /dev/null +++ b/utils/snap7_src/src/core/s7_types.h @@ -0,0 +1,1066 @@ +/*=============================================================================| +| 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/ | +|=============================================================================*/ +#ifndef s7_types_h +#define s7_types_h +//------------------------------------------------------------------------------ +#include "s7_isotcp.h" +//------------------------------------------------------------------------------ +// EXPORT CONSTANTS +// Everything added in this section has to be copied into wrappers interface +//------------------------------------------------------------------------------ + +#ifdef OS_WINDOWS +#define SM7API __stdcall +#else +#define SM7API +#endif + + // Area ID +const byte S7AreaPE = 0x81; +const byte S7AreaPA = 0x82; +const byte S7AreaMK = 0x83; +const byte S7AreaDB = 0x84; +const byte S7AreaCT = 0x1C; +const byte S7AreaTM = 0x1D; + +const int MaxVars = 20; + +const int S7WLBit = 0x01; +const int S7WLByte = 0x02; +const int S7WLChar = 0x03; +const int S7WLWord = 0x04; +const int S7WLInt = 0x05; +const int S7WLDWord = 0x06; +const int S7WLDInt = 0x07; +const int S7WLReal = 0x08; +const int S7WLCounter = 0x1C; +const int S7WLTimer = 0x1D; + + // Block type +const byte Block_OB = 0x38; +const byte Block_DB = 0x41; +const byte Block_SDB = 0x42; +const byte Block_FC = 0x43; +const byte Block_SFC = 0x44; +const byte Block_FB = 0x45; +const byte Block_SFB = 0x46; + + // Sub Block Type +const byte SubBlk_OB = 0x08; +const byte SubBlk_DB = 0x0A; +const byte SubBlk_SDB = 0x0B; +const byte SubBlk_FC = 0x0C; +const byte SubBlk_SFC = 0x0D; +const byte SubBlk_FB = 0x0E; +const byte SubBlk_SFB = 0x0F; + + // Block languages +const byte BlockLangAWL = 0x01; +const byte BlockLangKOP = 0x02; +const byte BlockLangFUP = 0x03; +const byte BlockLangSCL = 0x04; +const byte BlockLangDB = 0x05; +const byte BlockLangGRAPH = 0x06; + + // CPU status +const byte S7CpuStatusUnknown = 0x00; +const byte S7CpuStatusRun = 0x08; +const byte S7CpuStatusStop = 0x04; + +const longword evcSnap7Base = 0x00008000; +// S7 Server Event Code +const longword evcPDUincoming = 0x00010000; +const longword evcDataRead = 0x00020000; +const longword evcDataWrite = 0x00040000; +const longword evcNegotiatePDU = 0x00080000; +const longword evcReadSZL = 0x00100000; +const longword evcClock = 0x00200000; +const longword evcUpload = 0x00400000; +const longword evcDownload = 0x00800000; +const longword evcDirectory = 0x01000000; +const longword evcSecurity = 0x02000000; +const longword evcControl = 0x04000000; +const longword evcReserved_08000000 = 0x08000000; +const longword evcReserved_10000000 = 0x10000000; +const longword evcReserved_20000000 = 0x20000000; +const longword evcReserved_40000000 = 0x40000000; +const longword evcReserved_80000000 = 0x80000000; +// Event SubCodes +const word evsUnknown = 0x0000; +const word evsStartUpload = 0x0001; +const word evsStartDownload = 0x0001; +const word evsGetBlockList = 0x0001; +const word evsStartListBoT = 0x0002; +const word evsListBoT = 0x0003; +const word evsGetBlockInfo = 0x0004; +const word evsGetClock = 0x0001; +const word evsSetClock = 0x0002; +const word evsSetPassword = 0x0001; +const word evsClrPassword = 0x0002; +// Event Result +const word evrNoError = 0; +const word evrFragmentRejected = 0x0001; +const word evrMalformedPDU = 0x0002; +const word evrSparseBytes = 0x0003; +const word evrCannotHandlePDU = 0x0004; +const word evrNotImplemented = 0x0005; +const word evrErrException = 0x0006; +const word evrErrAreaNotFound = 0x0007; +const word evrErrOutOfRange = 0x0008; +const word evrErrOverPDU = 0x0009; +const word evrErrTransportSize = 0x000A; +const word evrInvalidGroupUData = 0x000B; +const word evrInvalidSZL = 0x000C; +const word evrDataSizeMismatch = 0x000D; +const word evrCannotUpload = 0x000E; +const word evrCannotDownload = 0x000F; +const word evrUploadInvalidID = 0x0010; +const word evrResNotFound = 0x0011; + + // Async mode +const int amPolling = 0; +const int amEvent = 1; +const int amCallBack = 2; + +//------------------------------------------------------------------------------ +// PARAMS LIST +// Notes for Local/Remote Port +// If the local port for a server and remote port for a client is != 102 they +// will be *no more compatible with S7 IsoTCP* +// A good reason to change them could be inside a debug session under Unix. +// Increasing the port over 1024 avoids the need of be root. +// Obviously you need to work with the couple Snap7Client/Snap7Server and change +// both, or, use iptable and nat the port. +//------------------------------------------------------------------------------ +const int p_u16_LocalPort = 1; +const int p_u16_RemotePort = 2; +const int p_i32_PingTimeout = 3; +const int p_i32_SendTimeout = 4; +const int p_i32_RecvTimeout = 5; +const int p_i32_WorkInterval = 6; +const int p_u16_SrcRef = 7; +const int p_u16_DstRef = 8; +const int p_u16_SrcTSap = 9; +const int p_i32_PDURequest = 10; +const int p_i32_MaxClients = 11; +const int p_i32_BSendTimeout = 12; +const int p_i32_BRecvTimeout = 13; +const int p_u32_RecoveryTime = 14; +const int p_u32_KeepAliveTime = 15; + +// Bool param is passed as int32_t : 0->false, 1->true +// String param (only set) is passed as pointer + +typedef int16_t *Pint16_t; +typedef uint16_t *Puint16_t; +typedef int32_t *Pint32_t; +typedef uint32_t *Puint32_t; +typedef int64_t *Pint64_t; +typedef uint64_t *Puint64_t; +typedef uintptr_t *Puintptr_t; +//----------------------------------------------------------------------------- +// INTERNALS CONSTANTS +//------------------------------------------------------------------------------ + +const word DBMaxName = 0xFFFF; // max number (name) of DB + +const longword errS7Mask = 0xFFF00000; +const longword errS7Base = 0x000FFFFF; +const longword errS7notConnected = errS7Base+0x0001; // Client not connected +const longword errS7InvalidMode = errS7Base+0x0002; // Requested a connection to... +const longword errS7InvalidPDUin = errS7Base+0x0003; // Malformed input PDU + +// S7 outcoming Error code +const word Code7Ok = 0x0000; +const word Code7AddressOutOfRange = 0x0005; +const word Code7InvalidTransportSize = 0x0006; +const word Code7WriteDataSizeMismatch = 0x0007; +const word Code7ResItemNotAvailable = 0x000A; +const word Code7ResItemNotAvailable1 = 0xD209; +const word Code7InvalidValue = 0xDC01; +const word Code7NeedPassword = 0xD241; +const word Code7InvalidPassword = 0xD602; +const word Code7NoPasswordToClear = 0xD604; +const word Code7NoPasswordToSet = 0xD605; +const word Code7FunNotAvailable = 0x8104; +const word Code7DataOverPDU = 0x8500; + +// Result transport size +const byte TS_ResBit = 0x03; +const byte TS_ResByte = 0x04; +const byte TS_ResInt = 0x05; +const byte TS_ResReal = 0x07; +const byte TS_ResOctet = 0x09; + +// Client Job status (lib internals, not S7) +const int JobComplete = 0; +const int JobPending = 1; + +// Control codes +const word CodeControlUnknown = 0; +const word CodeControlColdStart = 1; // Cold start +const word CodeControlWarmStart = 2; // Warm start +const word CodeControlStop = 3; // Stop +const word CodeControlCompress = 4; // Compress +const word CodeControlCpyRamRom = 5; // Copy Ram to Rom +const word CodeControlInsDel = 6; // Insert in working ram the block downloaded + // Delete from working ram the block selected +// PDU Type +const byte PduType_request = 1; // family request +const byte PduType_response = 3; // family response +const byte PduType_userdata = 7; // family user data + +// PDU Functions +const byte pduResponse = 0x02; // Response (when error) +const byte pduFuncRead = 0x04; // Read area +const byte pduFuncWrite = 0x05; // Write area +const byte pduNegotiate = 0xF0; // Negotiate PDU length +const byte pduStart = 0x28; // CPU start +const byte pduStop = 0x29; // CPU stop +const byte pduStartUpload = 0x1D; // Start Upload +const byte pduUpload = 0x1E; // Upload +const byte pduEndUpload = 0x1F; // EndUpload +const byte pduReqDownload = 0x1A; // Start Download request +const byte pduDownload = 0x1B; // Download request +const byte pduDownloadEnded = 0x1C; // Download end request +const byte pduControl = 0x28; // Control (insert/delete..) + +// PDU SubFunctions +const byte SFun_ListAll = 0x01; // List all blocks +const byte SFun_ListBoT = 0x02; // List Blocks of type +const byte SFun_BlkInfo = 0x03; // Get Block info +const byte SFun_ReadSZL = 0x01; // Read SZL +const byte SFun_ReadClock = 0x01; // Read Clock (Date and Time) +const byte SFun_SetClock = 0x02; // Set Clock (Date and Time) +const byte SFun_EnterPwd = 0x01; // Enter password for this session +const byte SFun_CancelPwd = 0x02; // Cancel password for this session +const byte SFun_Insert = 0x50; // Insert block +const byte SFun_Delete = 0x42; // Delete block + +typedef tm *PTimeStruct; + +//============================================================================== +// HEADERS +//============================================================================== +#pragma pack(1) + +// Tag Struct +typedef struct{ + int Area; + int DBNumber; + int Start; + int Size; + int WordLen; +}TS7Tag, *PS7Tag; + +// Incoming header, it will be mapped onto IsoPDU payload +typedef struct { + byte P; // Telegram ID, always 32 + byte PDUType; // Header type 1 or 7 + word AB_EX; // AB currently unknown, maybe it can be used for long numbers. + word Sequence; // Message ID. This can be used to make sure a received answer + word ParLen; // Length of parameters which follow this header + word DataLen; // Length of data which follow the parameters +}TS7ReqHeader; + +typedef TS7ReqHeader* PS7ReqHeader; + +// Outcoming 12 bytes header , response for Request type 1 +typedef struct{ + byte P; // Telegram ID, always 32 + byte PDUType; // Header type 2 or 3 + word AB_EX; // AB currently unknown, maybe it can be used for long numbers. + word Sequence; // Message ID. This can be used to make sure a received answer + word ParLen; // Length of parameters which follow this header + word DataLen; // Length of data which follow the parameters + word Error; // Error code +} TS7ResHeader23; + +typedef TS7ResHeader23* PS7ResHeader23; + +// Outcoming 10 bytes header , response for Request type 7 +typedef struct{ + byte P; // Telegram ID, always 32 + byte PDUType; // Header type 1 or 7 + word AB_EX; // AB currently unknown, maybe it can be used for long numbers. + word Sequence; // Message ID. This can be used to make sure a received answer + word ParLen; // Length of parameters which follow this header + word DataLen; // Length of data which follow the parameters +}TS7ResHeader17; + +typedef TS7ResHeader17* PS7ResHeader17; + +// Outcoming 10 bytes header , response for Request type 8 (server control) +typedef struct { + byte P; // Telegram ID, always 32 + byte PDUType; // Header type 8 + word AB_EX; // Zero + word Sequence; // Message ID. This can be used to make sure a received answer + word DataLen; // Length of data which follow this header + word Error; // Error code +} TS7ResHeader8; + +typedef TS7ResHeader8* PS7ResHeader8; + +// Outcoming answer buffer header type 2 or header type 3 +typedef struct{ + TS7ResHeader23 Header; + byte ResData [IsoPayload_Size - sizeof(TS7ResHeader23)]; +} TS7Answer23; + +typedef TS7Answer23* PS7Answer23; + +// Outcoming buffer header type 1 or header type 7 +typedef struct { + TS7ResHeader17 Header; + byte ResData [IsoPayload_Size - sizeof(TS7ResHeader17)]; +} TS7Answer17; + +typedef TS7Answer17* PS7Answer17; + +typedef byte TTimeBuffer[8]; +typedef byte *PTimeBuffer[8]; + +typedef struct{ + byte bcd_year; + byte bcd_mon; + byte bcd_day; + byte bcd_hour; + byte bcd_min; + byte bcd_sec; + byte bcd_himsec; + byte bcd_dow; +}TS7Time, *PS7Time; + +typedef byte TS7Buffer[65536]; +typedef byte *PS7Buffer; + +const int ReqHeaderSize = sizeof(TS7ReqHeader); +const int ResHeaderSize23 = sizeof(TS7ResHeader23); +const int ResHeaderSize17 = sizeof(TS7ResHeader17); + +// Most used request type parameters record +typedef struct { + byte Head[3];// 0x00 0x01 0x12 + byte Plen; // par len 0x04 + byte Uk; // unknown + byte Tg; // type and group (4 bits type and 4 bits group) + byte SubFun; // subfunction + byte Seq; // sequence +}TReqFunTypedParams; + +//============================================================================== +// FUNCTION NEGOTIATE +//============================================================================== +typedef struct { + byte FunNegotiate; + byte Unknown; + word ParallelJobs_1; + word ParallelJobs_2; + word PDULength; +}TReqFunNegotiateParams; + +typedef TReqFunNegotiateParams* PReqFunNegotiateParams; + +typedef struct { + byte FunNegotiate; + byte Unknown; + word ParallelJobs_1; + word ParallelJobs_2; + word PDULength; +}TResFunNegotiateParams; + +typedef TResFunNegotiateParams* PResFunNegotiateParams; + +//============================================================================== +// FUNCTION READ +//============================================================================== +typedef struct { + byte ItemHead[3]; + byte TransportSize; + word Length; + word DBNumber; + byte Area; + byte Address[3]; +}TReqFunReadItem, * PReqFunReadItem; + +//typedef TReqFunReadItem; + +typedef struct { + byte FunRead; + byte ItemsCount; + TReqFunReadItem Items[MaxVars]; +}TReqFunReadParams; + +typedef TReqFunReadParams* PReqFunReadParams; + +typedef struct { + byte FunRead; + byte ItemCount; +}TResFunReadParams; + +typedef TResFunReadParams* PResFunReadParams; + +typedef struct { + byte ReturnCode; + byte TransportSize; + word DataLength; + byte Data[IsoPayload_Size - 17]; // 17 = header + params + data header - 1 +}TResFunReadItem, *PResFunReadItem; + +typedef PResFunReadItem TResFunReadData[MaxVars]; + +//============================================================================== +// FUNCTION WRITE +//============================================================================== +typedef struct { + byte ItemHead[3]; + byte TransportSize; + word Length; + word DBNumber; + byte Area; + byte Address[3]; +}TReqFunWriteItem, * PReqFunWriteItem; + +typedef struct { + byte FunWrite; + byte ItemsCount; + TReqFunWriteItem Items[MaxVars]; +}TReqFunWriteParams; + +typedef TReqFunWriteParams* PReqFunWriteParams; + +typedef struct { + byte ReturnCode; + byte TransportSize; + word DataLength; + byte Data [IsoPayload_Size - 17]; // 17 = header + params + data header -1 +}TReqFunWriteDataItem, *PReqFunWriteDataItem; + +typedef PReqFunWriteDataItem TReqFunWriteData[MaxVars]; + +typedef struct { + byte FunWrite; + byte ItemCount; + byte Data[MaxVars]; +}TResFunWrite; + +typedef TResFunWrite* PResFunWrite; + +//============================================================================== +// GROUP UPLOAD +//============================================================================== +typedef struct { + byte FunSUpld; // function start upload 0x1D + byte Uk6 [6]; // Unknown 6 bytes + byte Upload_ID; + byte Len_1; + byte Prefix; + byte BlkPrfx; // always 0x30 + byte BlkType; + byte AsciiBlk[5]; // BlockNum in ascii + byte A; // always 0x41 ('A') +}TReqFunStartUploadParams; + +typedef TReqFunStartUploadParams* PReqFunStartUploadParams; + +typedef struct { + byte FunSUpld; // function start upload 0x1D + byte Data_1[6]; + byte Upload_ID; + byte Uk[3]; + byte LenLoad[5]; +}TResFunStartUploadParams; + +typedef TResFunStartUploadParams* PResFunStartUploadParams; + +typedef struct { + byte FunUpld; // function upload 0x1E + byte Uk6[6]; // Unknown 6 bytes + byte Upload_ID; +}TReqFunUploadParams; + +typedef TReqFunUploadParams* PReqFunUploadParams; + +typedef struct { + byte FunUpld; // function upload 0x1E + byte EoU; // 0 = End Of Upload, 1 = Upload in progress +}TResFunUploadParams; + +typedef TResFunUploadParams* PResFunUploadParams; + +typedef struct { + word Length; // Payload length - 4 + byte Uk_00; // Unknown 0x00 + byte Uk_FB; // Unknown 0xFB + // from here is the same of TS7CompactBlockInfo + word Cst_pp; + byte Uk_01; // Unknown 0x01 + byte BlkFlags; + byte BlkLang; + byte SubBlkType; + word BlkNum; + u_int LenLoadMem; + u_int BlkSec; + u_int CodeTime_ms; + word CodeTime_dy; + u_int IntfTime_ms; + word IntfTime_dy; + word SbbLen; + word AddLen; + word LocDataLen; + word MC7Len; +}TResFunUploadDataHeaderFirst; + +typedef TResFunUploadDataHeaderFirst* PResFunUploadDataHeaderFirst; + +typedef struct { + word Length;// Payload length - 4 + byte Uk_00; // Unknown 0x00 + byte Uk_FB; // Unknown 0xFB +}TResFunUploadDataHeaderNext; + +typedef TResFunUploadDataHeaderNext* PResFunUploadDataHeaderNext; + +typedef struct { + word Length;// Payload length - 4 + byte Uk_00; // Unknown 0x00 + byte Uk_FB; // Unknown 0xFB +}TResFunUploadDataHeader; + +typedef TResFunUploadDataHeader* PResFunUploadDataHeader; + +typedef struct { + byte ID; // 0x65 + word Seq; // Sequence + byte Const_1[10]; + word Lo_bound; + word Hi_Bound; + byte u_shortLen;// 0x02 byte + // 0x04 word + // 0x05 int + // 0x06 dword + // 0x07 dint + // 0x08 real + byte c1, c2; + char Author[8]; + char Family[8]; + char Header[8]; + byte B1; // 0x11 + byte B2; // 0x00 + word Chksum; + byte Uk_8[8]; +}TArrayUpldFooter; + +typedef TArrayUpldFooter* PArrayUpldFooter; + +typedef struct { + byte FunEUpld; // function end upload 0x1F + byte Uk6[6]; // Unknown 6 bytes + byte Upload_ID; +}TReqFunEndUploadParams; + +typedef TReqFunEndUploadParams* PReqFunEndUploadParams; + +typedef struct { + byte FunEUpld; // function end upload 0x1F +}TResFunEndUploadParams; + +typedef TResFunEndUploadParams* PResFunEndUploadParams; + +//============================================================================== +// GROUP DOWNLOAD +//============================================================================== +typedef struct { + byte FunSDwnld; // function start Download 0x1A + byte Uk6[6]; // Unknown 6 bytes + byte Dwnld_ID; + byte Len_1; // 0x09 + byte Prefix; // 0x5F + byte BlkPrfx; // always 0x30 + byte BlkType; + byte AsciiBlk[5]; // BlockNum in ascii + byte P; // 0x50 ('P') + byte Len_2; // 0x0D + byte Uk1; // 0x01 + byte AsciiLoad[6];// load memory size (MC7 size + 92) + byte AsciiMC7[6]; // Block size in bytes +}TReqStartDownloadParams; + +typedef TReqStartDownloadParams* PReqStartDownloadParams; +typedef byte TResStartDownloadParams; +typedef TResStartDownloadParams* PResStartDownloadParams; + +typedef struct { + byte Fun; // pduDownload or pduDownloadEnded + byte Uk7[7]; + byte Len_1; // 0x09 + byte Prefix; // 0x5F + byte BlkPrfx; // always 0x30 + byte BlkType; + byte AsciiBlk[5]; // BlockNum in ascii + byte P; // 0x50 ('P') +}TReqDownloadParams; + +typedef TReqDownloadParams* PReqDownloadParams; + +typedef struct { + byte FunDwnld; // 0x1B + byte EoS; // End of sequence : 0x00 - Sequence in progress : 0x01 +}TResDownloadParams; + +typedef TResDownloadParams* PResDownloadParams; + +typedef struct { + word DataLen; + word FB_00; // 0x00 0xFB +}TResDownloadDataHeader; + +typedef TResDownloadDataHeader* PResDownloadDataHeader; +typedef byte TResEndDownloadParams; +typedef TResEndDownloadParams* PResEndDownloadParams; + +typedef struct { + word Cst_pp; + byte Uk_01; // Unknown 0x01 + byte BlkFlags; + byte BlkLang; + byte SubBlkType; + word BlkNum; + u_int LenLoadMem; + u_int BlkSec; + u_int CodeTime_ms; + word CodeTime_dy; + u_int IntfTime_ms; + word IntfTime_dy; + word SbbLen; + word AddLen; + word LocDataLen; + word MC7Len; +}TS7CompactBlockInfo; + +typedef TS7CompactBlockInfo* PS7CompactBlockInfo; + +typedef struct { + byte Uk_20[20]; + byte Author[8]; + byte Family[8]; + byte Header[8]; + byte B1; // 0x11 + byte B2; // 0x00 + word Chksum; + byte Uk_12[8]; +}TS7BlockFooter; + +typedef TS7BlockFooter* PS7BlockFooter; + +//============================================================================== +// FUNCTION INSERT/DELETE +//============================================================================== +typedef struct { + byte Fun; // plc control 0x28 + byte Uk7[7]; // unknown 7 + word Len_1; // Length part 1 : 10 + byte NumOfBlocks; // number of blocks to insert + byte ByteZero; // 0x00 + byte AsciiZero; // 0x30 '0' + byte BlkType; + byte AsciiBlk[5]; // BlockNum in ascii + byte SFun; // 0x50 or 0x42 + byte Len_2; // Length part 2 : 0x05 bytes + char Cmd[5]; // ascii '_INSE' or '_DELE' +}TReqControlBlockParams; + +typedef TReqControlBlockParams* PReqControlBlockParams; + +//============================================================================== +// FUNCTIONS START/STOP/COPY RAM TO ROM/COMPRESS +//============================================================================== +typedef struct { + byte Fun; // stop 0x29 + byte Uk_5[5]; // unknown 5 bytes 0x00 + byte Len_2; // Length part 2 : 0x09 + char Cmd[9]; // ascii 'P_PROGRAM' +}TReqFunPlcStop; + +typedef TReqFunPlcStop* PReqFunPlcStop; + +typedef struct { + byte Fun; // start 0x28 + byte Uk_7[7]; // unknown 7 + word Len_1; // Length part 1 : 0x0000 + byte Len_2; // Length part 2 : 0x09 + char Cmd [9]; // ascii 'P_PROGRAM' +}TReqFunPlcHotStart; + +typedef TReqFunPlcHotStart* PReqFunPlcHotStart; + +typedef struct { + byte Fun; // start 0x28 + byte Uk_7[7]; // unknown 7 + word Len_1; // Length part 1 : 0x0002 + word SFun; // 'C ' 0x4320 + byte Len_2; // Length part 2 : 0x09 + char Cmd[9]; // ascii 'P_PROGRAM' +}TReqFunPlcColdStart; + +typedef TReqFunPlcColdStart* PReqFunPlcColdStart; + +typedef struct { + byte Fun; // pduControl 0x28 + byte Uk_7[7]; // unknown 7 + word Len_1; // Length part 1 : 0x0002 + word SFun; // 'EP' 0x4550 + byte Len_2; // Length part 2 : 0x05 + char Cmd[5]; // ascii '_MODU' +}TReqFunCopyRamToRom; + +typedef TReqFunCopyRamToRom* PReqFunCopyRamToRom; + +typedef struct { + byte Fun; // pduControl 0x28 + byte Uk_7[7]; // unknown 7 + word Len_1; // Length part 1 : 0x00 + byte Len_2; // Length part 2 : 0x05 + char Cmd[5]; // ascii '_GARB' +}TReqFunCompress; + +typedef TReqFunCompress* PReqFunCompress; + +typedef struct { + byte ResFun; + byte para; +}TResFunCtrl; + +typedef TResFunCtrl* PResFunCtrl; + +//============================================================================== +// FUNCTIONS USERDATA +//============================================================================== +typedef struct { + byte Head[3]; // Always 0x00 0x01 0x12 + byte Plen; // par len 0x04 or 0x08 + byte Uk; // unknown + byte Tg; // type and group (4 bits type and 4 bits group) + byte SubFun; // subfunction + byte Seq; // sequence + word resvd; // present if plen=0x08 (S7 manager online functions) + word Err; // present if plen=0x08 (S7 manager online functions) +}TS7Params7; + +typedef TS7Params7* PS7ReqParams7; +typedef TS7Params7* PS7ResParams7; + +// for convenience Hi order bit of type are included (0x4X) +const byte grProgrammer = 0x41; +const byte grCyclicData = 0x42; +const byte grBlocksInfo = 0x43; +const byte grSZL = 0x44; +const byte grPassword = 0x45; +const byte grBSend = 0x46; +const byte grClock = 0x47; +const byte grSecurity = 0x45; + +//============================================================================== +// GROUP SECURITY +//============================================================================== +typedef TReqFunTypedParams TReqFunSecurity; +typedef TReqFunSecurity* PReqFunSecurity; + +typedef char TS7Password[8]; + +typedef struct { + byte Ret; // 0xFF for request + byte TS; // 0x09 Transport size + word DLen; // Data len : 8 bytes + byte Pwd[8]; // Password encoded into "AG" format +}TReqDataSecurity; + +typedef TReqDataSecurity* PReqDataSecurity; +typedef TS7Params7 TResParamsSecurity; +typedef TResParamsSecurity* PResParamsSecurity; + +typedef struct { + byte Ret; + byte TS; + word DLen; +}TResDataSecurity; + +typedef TResDataSecurity* PResDataSecurity; + +//============================================================================== +// GROUP BLOCKS SZL +//============================================================================== +typedef TReqFunTypedParams TReqFunReadSZLFirst; +typedef TReqFunReadSZLFirst* PReqFunReadSZLFirst; + +typedef struct { + byte Head[3]; // 0x00 0x01 0x12 + byte Plen; // par len 0x04 + byte Uk; // unknown + byte Tg; // type and group (4 bits type and 4 bits group) + byte SubFun; // subfunction + byte Seq; // sequence + word Rsvd; // Reserved 0x0000 + word ErrNo; // Error Code +}TReqFunReadSZLNext; + +typedef TReqFunReadSZLNext* PReqFunReadSZLNext; + +typedef struct { + byte Ret; // 0xFF for request + byte TS; // 0x09 Transport size + word DLen; // Data len + word ID; // SZL-ID + word Index;// SZL-Index +}TS7ReqSZLData; + +typedef TS7ReqSZLData* PS7ReqSZLData; + +typedef struct { + byte Ret; + byte TS; + word DLen; + word ID; + word Index; + word ListLen; + word ListCount; + word Data[32747]; +}TS7ResSZLDataFirst; + +typedef TS7ResSZLDataFirst* PS7ResSZLDataFirst; + +typedef struct { + byte Ret; + byte TS; + word DLen; + word Data[32751]; +}TS7ResSZLDataNext; + +typedef TS7ResSZLDataNext* PS7ResSZLDataNext; + +typedef struct { + byte Ret; + byte OtherInfo[9]; + word Count; + word Items[32747]; +}TS7ResSZLData_0; + +typedef TS7ResSZLData_0* PS7ResSZLData_0; + +//============================================================================== +// GROUP CLOCK +//============================================================================== +typedef TReqFunTypedParams TReqFunDateTime; +typedef TReqFunDateTime* PReqFunDateTime; + +typedef byte TReqDataGetDateTime[4]; + +typedef longword *PReqDataGetDateTime; + +typedef struct { + byte RetVal; + byte TSize; + word Length; + byte Rsvd; + byte HiYear; + TTimeBuffer Time; +}TResDataGetTime; + +typedef TResDataGetTime* PResDataGetTime; +typedef TResDataGetTime TReqDataSetTime; +typedef TReqDataSetTime* PReqDataSetTime; + +typedef struct { + byte RetVal; + byte TSize; + word Length; +}TResDataSetTime; + +typedef TResDataSetTime* PResDataSetTime; + +//============================================================================== +// GROUP BLOCKS INFO +//============================================================================== +typedef TReqFunTypedParams TReqFunGetBlockInfo; +typedef TReqFunGetBlockInfo* PReqFunGetBlockInfo; + +typedef byte TReqDataFunBlocks[4]; +typedef u_char* PReqDataFunBlocks; + +typedef struct { + byte Head[3]; // 0x00 0x01 0x12 + byte Plen; // par len 0x04 + byte Uk; // unknown + byte Tg; // type and group (4 bits type and 4 bits group) + byte SubFun; // subfunction + byte Seq; // sequence + word Rsvd; // Reserved 0x0000 + word ErrNo; // Error Code +}TResFunGetBlockInfo; + +typedef TResFunGetBlockInfo* PResFunGetBlockInfo; + +typedef struct { + byte Zero; // always 0x30 -> Ascii 0 + byte BType; // Block Type + word BCount; // Block count +}TResFunGetBlockItem; + +typedef struct { + byte RetVal; + byte TRSize; + word Length; + TResFunGetBlockItem Blocks[7]; +}TDataFunListAll; + +typedef TDataFunListAll* PDataFunListAll; + +typedef struct { + word BlockNum; + byte Unknown; + byte BlockLang; +}TDataFunGetBotItem; + +typedef struct { + byte RetVal; + byte TSize; + word DataLen; + TDataFunGetBotItem Items[(IsoPayload_Size - 29 ) / 4]; +}TDataFunGetBot; +// Note : 29 is the size of headers iso, COPT, S7 header, params, data + +typedef TDataFunGetBot* PDataFunGetBot; + +typedef struct { + byte RetVal; // 0xFF + byte TSize; // Octet (0x09) + word Length; // 0x0002 + byte Zero; // Ascii '0' (0x30) + byte BlkType; +}TReqDataBlockOfType; + +typedef TReqDataBlockOfType* PReqDataBlockOfType; + +typedef struct { + byte RetVal; + byte TSize; + word DataLen; + byte BlkPrfx; // always 0x30 + byte BlkType; + byte AsciiBlk[5]; // BlockNum in ascii + byte A; // always 0x41 ('A') +}TReqDataBlockInfo; + +typedef TReqDataBlockInfo* PReqDataBlockInfo; + +typedef struct { + byte RetVal; + byte TSize; + word Length; + byte Cst_b; + byte BlkType; + word Cst_w1; + word Cst_w2; + word Cst_pp; + byte Unknown_1; + byte BlkFlags; + byte BlkLang; + byte SubBlkType; + word BlkNumber; + u_int LenLoadMem; + byte BlkSec[4]; + u_int CodeTime_ms; + word CodeTime_dy; + u_int IntfTime_ms; + word IntfTime_dy; + word SbbLen; + word AddLen; + word LocDataLen; + word MC7Len; + byte Author[8]; + byte Family[8]; + byte Header[8]; + byte Version; + byte Unknown_2; + word BlkChksum; + byte Resvd1[4]; + byte Resvd2[4]; +}TResDataBlockInfo; + +typedef TResDataBlockInfo* PResDataBlockInfo; + +//============================================================================== +// BSEND / BRECV +//============================================================================== +typedef struct { + int Size; + longword R_ID; + byte Data[65536]; +}TPendingBuffer; + +typedef struct { + TTPKT TPKT; + TCOTP_DT COTP; + byte P; + byte PDUType; +}TPacketInfo; + +typedef struct { + byte Head[3];// Always 0x00 0x01 0x12 + byte Plen; // par len 0x04 or 0x08 + byte Uk; // unknown (0x12) + byte Tg; // type and group, 4 bits type and 4 bits group (0x46) + byte SubFun; // subfunction (0x01) + byte Seq; // sequence + byte IDSeq; // ID Sequence (come from partner) + byte EoS; // End of Sequence = 0x00 Sequence in progress = 0x01; + word Err; // +}TBSendParams; + +typedef TBSendParams* PBSendReqParams; +typedef TBSendParams* PBSendResParams; + +// Data frame + +typedef struct { + byte FF; // 0xFF + byte TRSize; // Transport Size 0x09 (octet) + word Len; // This Telegram Length + byte DHead[4];// sequence 0x12 0x06 0x13 0x00 + u_int R_ID; // R_ID +}TBsendRequestData; + +typedef TBsendRequestData* PBsendRequestData; + +typedef struct { + byte DHead[4]; // sequence 0x0A 0x00 0x00 0x00 +}TBSendResData; + +typedef TBSendResData* PBSendResData; + +#pragma pack() +#endif // s7_types_h