59 Commits

Author SHA1 Message Date
Thiago Alves
76036ec9a7 Update release 2025-03-17 14:59:22 -04:00
Thiago Alves
6ab236ee46 Merge pull request #267 from davenardella/master
S7 Protocol implementation
2025-03-17 14:58:38 -04:00
Dave Nardella
d4d2a2a151 Added S7 Protocol inclusion 2025-03-15 13:46:23 +01:00
Dave Nardella
ca997e5b16 Added S7 Protocol support 2025-03-15 13:45:33 +01:00
Dave Nardella
f1577320e6 Fixed %MW0 access 2025-03-15 12:27:17 +01:00
Dave Nardella
8d570a1e73 Added S7 Protocol support 2025-03-15 12:23:12 +01:00
Dave Nardella
57d115dd3d OpenPLC Snap7 wrappers 2025-03-15 12:21:48 +01:00
Dave Nardella
07becdbd26 Add files via upload 2025-03-15 12:21:14 +01:00
Dave Nardella
685ebf8556 S7 sys files 2025-03-15 12:20:44 +01:00
Dave Nardella
f3da2df734 Add files via upload 2025-03-15 12:20:11 +01:00
Dave Nardella
ddcae72b01 S7 Lib files 2025-03-15 12:19:42 +01:00
Dave Nardella
6eef098fcb Add files via upload 2025-03-15 12:19:08 +01:00
Dave Nardella
7d93319f71 S7 core files 2025-03-15 12:18:41 +01:00
Dave Nardella
4570809599 Add files via upload 2025-03-15 12:18:02 +01:00
Dave Nardella
bcfd91af33 Sources folder 2025-03-15 12:17:25 +01:00
Dave Nardella
cad11eef1b Build folder 2025-03-15 12:16:59 +01:00
Dave Nardella
61aba319ec linux makefile (X86/X64/ARM) 2025-03-15 12:16:11 +01:00
Dave Nardella
2ea0ebc1bd Win64 binaries DLL and LIB 2025-03-15 12:15:21 +01:00
Dave Nardella
812d200200 Win32 binaries DLL and LIB 2025-03-15 12:14:58 +01:00
Dave Nardella
f0465d7426 Update readme.txt 2025-03-15 12:13:36 +01:00
Dave Nardella
947de75ae1 updated readme.txt 2025-03-15 12:12:41 +01:00
Dave Nardella
b47b2ebd57 Create readme.txt 2025-03-15 12:12:12 +01:00
Dave Nardella
2b762ef921 Binary folder 2025-03-15 12:11:21 +01:00
Dave Nardella
792395af0f Create dummy.txt 2025-03-15 12:10:45 +01:00
Dave Nardella
a6fbbd93d0 Create dummy.txt 2025-03-15 12:10:06 +01:00
Dave Nardella
685cde24c6 Create dummy.txt 2025-03-15 12:09:49 +01:00
Dave Nardella
2dbcdc9336 Create dummy.txt 2025-03-15 12:09:32 +01:00
Dave Nardella
c18d214efd Create dummy.txt 2025-03-15 12:08:46 +01:00
Dave Nardella
734e7393c0 Update readme.txt 2025-03-15 12:07:26 +01:00
Dave Nardella
a735060b1f Create readme.txt 2025-03-15 12:04:54 +01:00
Dave Nardella
02754b3405 Update readme.txt 2025-03-15 12:04:28 +01:00
Dave Nardella
b64c74be55 Create readme.txt 2025-03-15 12:03:35 +01:00
Dave Nardella
7cfc8f65a1 Add files via upload 2025-03-15 12:02:27 +01:00
Dave Nardella
6c03b965fd Create dummy.txt 2025-03-15 12:02:10 +01:00
Dave Nardella
71b891ac16 Add files via upload 2025-03-15 12:00:57 +01:00
Dave Nardella
13a6c8ee7f Add files via upload 2025-03-15 12:00:24 +01:00
Dave Nardella
c93f3cee81 Create dummy.txt 2025-03-15 11:59:32 +01:00
Dave Nardella
a999b4749d Create dummy.txt 2025-03-15 11:59:12 +01:00
Dave Nardella
9ebadf8ccb Create dummy.txt 2025-03-15 11:58:32 +01:00
Dave Nardella
ac6dae30c3 Create dummy.txt 2025-03-15 11:58:08 +01:00
Dave Nardella
4bb230a9c0 Create readme.txt 2025-03-15 11:56:42 +01:00
Dave Nardella
10491c693c Update readme.txt 2025-03-15 11:55:44 +01:00
Dave Nardella
f997d091bd Create readme.txt 2025-03-15 11:55:06 +01:00
Dave Nardella
d47d41e52a Added S7-Protocol documentation 2025-03-15 11:51:41 +01:00
Dave Nardella
259e0543a5 Add files via upload 2025-03-15 11:50:53 +01:00
Dave Nardella
b25902e4c8 Create readme.txt 2025-03-15 11:50:37 +01:00
Dave Nardella
9e3ccdfc3b Delete documentation/S7-Protocol directory 2025-03-15 11:49:47 +01:00
Dave Nardella
35f2845e59 Delete documentation/S7-Protocol/readme.txt 2025-03-15 11:48:46 +01:00
Dave Nardella
d83b70e60c Add files via upload 2025-03-15 11:47:55 +01:00
Dave Nardella
698da2dc6c Create readme.txt 2025-03-15 11:47:40 +01:00
Dave Nardella
052ba55abc Delete documentation/S7-Protocol directory 2025-03-15 11:46:59 +01:00
Dave Nardella
12399b0b2a Delete documentation/S7-Protocol/readme.txt 2025-03-15 11:45:51 +01:00
Dave Nardella
931c1d41f7 Add files via upload 2025-03-15 11:45:18 +01:00
Dave Nardella
61ed23f58a Create readme.txt 2025-03-15 11:44:48 +01:00
Dave Nardella
5556aeb587 Added S7 Protocol inclusion 2025-03-15 11:40:19 +01:00
Dave Nardella
dc115011c6 New S7 Option file 2025-03-15 11:37:27 +01:00
Dave Nardella
b46e93d063 Fixed %MW0 access 2025-03-15 11:34:25 +01:00
Dave Nardella
8a9581590e Added S7 Protocol support 2025-03-15 11:33:18 +01:00
Dave Nardella
4e0d67323b Add files via upload
Fixed %MW0 access
2025-03-15 11:28:30 +01:00
49 changed files with 19334 additions and 21 deletions

View File

@@ -212,6 +212,14 @@ function install_libmodbus {
fi
}
function install_libsnap7 {
echo "[LIBSNAP7]"
cd "$OPENPLC_DIR/utils/snap7_src/build/linux"
$1 make clean
$1 make install || fail "Error installing Libsnap7"
cd "$OPENPLC_DIR"
}
function install_systemd_service() {
if [ "$1" == "sudo" ]; then
echo "[OPENPLC SERVICE]"
@@ -245,6 +253,7 @@ function install_all_libs {
install_opendnp3 "$1"
disable_ethercat "$1"
install_libmodbus "$1"
install_libsnap7 "$1"
}
function finalize_install {

View File

@@ -0,0 +1 @@
This folder is empty, it will contain the linux library after the build

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,111 @@
#
# Common for every unix flavour (any changes will be reflected on all platforms)
#
CXXFLAGS := -O3 -fPIC -pedantic
ConfigurationName :=Release
IntermediateDirectory :=../temp
OutDir := $(IntermediateDirectory)
LinkerName :=g++
SharedObjectLinkerName :=g++ -shared -fPIC
DebugSwitch :=-gstab
IncludeSwitch :=-I
LibrarySwitch :=-l
OutputSwitch :=-o
LibraryPathSwitch :=-L
PreprocessorSwitch :=-D
SourceSwitch :=-c
OutputFile :=../bin/linux/libsnap7.so
PreprocessOnlySwitch :=-E
ObjectsFileList :="filelist.txt"
MakeDirCommand :=mkdir -p
LinkOptions := -O3
IncludePath := $(IncludeSwitch). $(IncludeSwitch)../../src/sys $(IncludeSwitch)../../src/core $(IncludeSwitch)../../src/lib
Libs := $(LibrarySwitch)pthread $(LibrarySwitch)rt
LibPath := $(LibraryPathSwitch).
LibInstall := /usr/lib
##
## Common variables (CXXFLAGS varies across platforms)
##
AR := ar rcus
CXX := g++
CC := gcc
CFLAGS :=
##
## User defined environment variables
##
Objects0=$(IntermediateDirectory)/sys_snap_msgsock.o $(IntermediateDirectory)/sys_snap_sysutils.o $(IntermediateDirectory)/sys_snap_tcpsrvr.o $(IntermediateDirectory)/sys_snap_threads.o $(IntermediateDirectory)/core_s7_client.o $(IntermediateDirectory)/core_s7_isotcp.o $(IntermediateDirectory)/core_s7_partner.o $(IntermediateDirectory)/core_s7_peer.o $(IntermediateDirectory)/core_s7_server.o $(IntermediateDirectory)/core_s7_text.o \
$(IntermediateDirectory)/core_s7_micro_client.o $(IntermediateDirectory)/lib_snap7_libmain.o
Objects=$(Objects0)
##
## Main Build Targets
##
.PHONY: all clean install PreBuild PostBuild
all: $(OutputFile)
$(OutputFile): $(IntermediateDirectory)/.d $(Objects)
@$(MakeDirCommand) $(@D)
@$(MakeDirCommand) $(IntermediateDirectory)
@echo $(Objects0) > $(ObjectsFileList)
$(SharedObjectLinkerName) $(OutputSwitch)$(OutputFile) @$(ObjectsFileList) $(LibPath) $(Libs) $(LinkOptions)
$(RM) $(ObjectsFileList)
$(IntermediateDirectory)/.d:
@test -d ../temp || $(MakeDirCommand) ../temp
PreBuild:
PostBuild:
##
## Objects
##
$(IntermediateDirectory)/sys_snap_msgsock.o:
$(CXX) $(SourceSwitch) "../../src/sys/snap_msgsock.cpp" $(CXXFLAGS) -o $(IntermediateDirectory)/sys_snap_msgsock.o $(IncludePath)
$(IntermediateDirectory)/sys_snap_sysutils.o:
$(CXX) $(SourceSwitch) "../../src/sys/snap_sysutils.cpp" $(CXXFLAGS) -o $(IntermediateDirectory)/sys_snap_sysutils.o $(IncludePath)
$(IntermediateDirectory)/sys_snap_tcpsrvr.o:
$(CXX) $(SourceSwitch) "../../src/sys/snap_tcpsrvr.cpp" $(CXXFLAGS) -o $(IntermediateDirectory)/sys_snap_tcpsrvr.o $(IncludePath)
$(IntermediateDirectory)/sys_snap_threads.o:
$(CXX) $(SourceSwitch) "../../src/sys/snap_threads.cpp" $(CXXFLAGS) -o $(IntermediateDirectory)/sys_snap_threads.o $(IncludePath)
$(IntermediateDirectory)/core_s7_client.o:
$(CXX) $(SourceSwitch) "../../src/core/s7_client.cpp" $(CXXFLAGS) -o $(IntermediateDirectory)/core_s7_client.o $(IncludePath)
$(IntermediateDirectory)/core_s7_isotcp.o:
$(CXX) $(SourceSwitch) "../../src/core/s7_isotcp.cpp" $(CXXFLAGS) -o $(IntermediateDirectory)/core_s7_isotcp.o $(IncludePath)
$(IntermediateDirectory)/core_s7_partner.o:
$(CXX) $(SourceSwitch) "../../src/core/s7_partner.cpp" $(CXXFLAGS) -o $(IntermediateDirectory)/core_s7_partner.o $(IncludePath)
$(IntermediateDirectory)/core_s7_peer.o:
$(CXX) $(SourceSwitch) "../../src/core/s7_peer.cpp" $(CXXFLAGS) -o $(IntermediateDirectory)/core_s7_peer.o $(IncludePath)
$(IntermediateDirectory)/core_s7_server.o:
$(CXX) $(SourceSwitch) "../../src/core/s7_server.cpp" $(CXXFLAGS) -o $(IntermediateDirectory)/core_s7_server.o $(IncludePath)
$(IntermediateDirectory)/core_s7_text.o:
$(CXX) $(SourceSwitch) "../../src/core/s7_text.cpp" $(CXXFLAGS) -o $(IntermediateDirectory)/core_s7_text.o $(IncludePath)
$(IntermediateDirectory)/core_s7_micro_client.o:
$(CXX) $(SourceSwitch) "../../src/core/s7_micro_client.cpp" $(CXXFLAGS) -o $(IntermediateDirectory)/core_s7_micro_client.o $(IncludePath)
$(IntermediateDirectory)/lib_snap7_libmain.o:
$(CXX) $(SourceSwitch) "../../src/lib/snap7_libmain.cpp" $(CXXFLAGS) -o $(IntermediateDirectory)/lib_snap7_libmain.o $(IncludePath)
##
## Clean / Install
##
clean:
$(RM) $(IntermediateDirectory)/*.o
$(RM) $(OutputFile)
install: all
cp -f $(OutputFile) $(LibInstall)

View File

@@ -0,0 +1 @@
This folder is empty, will contain intermediate files after the installation (you can delete the content)

View File

@@ -0,0 +1,3 @@
There is no Windows installation makefile.
Instead, DLLs build witn Visual Studio 2022 are used, because they are smaller and faster.
Anyway, if you want, the Snap7 distribution contains the makefiles to build the library using mingw(32/64).

View File

@@ -0,0 +1 @@
This folder contains S7 Protocol implementation sources

View File

@@ -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();
}
};
}

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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->HLength<sizeof( TCOTP_DT )-1 ) || // Checks ISO 8073 header length
( Info->PDUType!=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 (IsoLen<DataHeaderSize )
{
PduKind=pkInvalidPDU;
return;
};
// Here IsoLen>DataHeaderSize : 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;
};
}

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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

View File

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

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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

View File

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

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,129 @@
LIBRARY SNAP7.DLL
EXPORTS
Cli_Create
Cli_Destroy
Cli_ConnectTo
Cli_SetConnectionParams
Cli_SetConnectionType
Cli_Connect
Cli_Disconnect
Cli_GetParam
Cli_SetParam
Cli_SetAsCallback
Cli_ReadArea
Cli_WriteArea
Cli_ReadMultiVars
Cli_WriteMultiVars
Cli_DBRead
Cli_DBWrite
Cli_MBRead
Cli_MBWrite
Cli_EBRead
Cli_EBWrite
Cli_ABRead
Cli_ABWrite
Cli_TMRead
Cli_TMWrite
Cli_CTRead
Cli_CTWrite
Cli_ListBlocks
Cli_GetAgBlockInfo
Cli_GetPgBlockInfo
Cli_ListBlocksOfType
Cli_Upload
Cli_FullUpload
Cli_Download
Cli_Delete
Cli_DBGet
Cli_DBFill
Cli_GetPlcDateTime
Cli_SetPlcDateTime
Cli_SetPlcSystemDateTime
Cli_GetOrderCode
Cli_GetCpuInfo
Cli_GetCpInfo
Cli_ReadSZL
Cli_ReadSZLList
Cli_PlcHotStart
Cli_PlcColdStart
Cli_PlcStop
Cli_CopyRamToRom
Cli_Compress
Cli_GetPlcStatus
Cli_GetProtection
Cli_SetSessionPassword
Cli_ClearSessionPassword
Cli_IsoExchangeBuffer
Cli_GetExecTime
Cli_GetLastError
Cli_GetPduLength
Cli_AsReadArea
Cli_AsWriteArea
Cli_AsDBRead
Cli_AsDBWrite
Cli_AsMBRead
Cli_AsMBWrite
Cli_AsEBRead
Cli_AsEBWrite
Cli_AsABRead
Cli_AsABWrite
Cli_AsTMRead
Cli_AsTMWrite
Cli_AsCTRead
Cli_AsCTWrite
Cli_AsListBlocksOfType
Cli_AsReadSZL
Cli_AsReadSZLList
Cli_AsUpload
Cli_AsFullUpload
Cli_AsDownload
Cli_AsCopyRamToRom
Cli_AsCompress
Cli_AsDBGet
Cli_AsDBFill
Cli_CheckAsCompletion
Cli_WaitAsCompletion
Cli_ErrorText
Cli_GetConnected
Srv_Create
Srv_Destroy
Srv_GetParam
Srv_SetParam
Srv_StartTo
Srv_Start
Srv_Stop
Srv_RegisterArea
Srv_UnregisterArea
Srv_LockArea
Srv_UnlockArea
Srv_GetStatus
Srv_SetCpuStatus
Srv_ClearEvents
Srv_PickEvent
Srv_GetMask
Srv_SetMask
Srv_SetEventsCallback
Srv_SetReadEventsCallback
Srv_SetRWAreaCallback
Srv_ErrorText
Srv_EventText
Par_Create
Par_Destroy
Par_GetParam
Par_SetParam
Par_StartTo
Par_Start
Par_Stop
Par_BSend
Par_AsBSend
Par_CheckAsBSendCompletion
Par_WaitAsBSendCompletion
Par_SetSendCallback
Par_BRecv
Par_CheckAsBRecvCompletion
Par_SetRecvCallback
Par_GetTimes
Par_GetStats
Par_GetLastError
Par_GetStatus
Par_ErrorText

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,201 @@
/*=============================================================================|
| 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 snap7_libmain_h
#define snap7_libmain_h
//---------------------------------------------------------------------------
#include "s7_client.h"
#include "s7_server.h"
#include "s7_partner.h"
#include "s7_text.h"
//---------------------------------------------------------------------------
const int mkEvent = 0;
const int mkLog = 1;
typedef uintptr_t S7Object; // multi platform/processor object reference
//==============================================================================
// CLIENT EXPORT LIST - Sync functions
//==============================================================================
EXPORTSPEC S7Object S7API Cli_Create();
EXPORTSPEC void S7API Cli_Destroy(S7Object &Client);
EXPORTSPEC int S7API Cli_Connect(S7Object Client);
EXPORTSPEC int S7API Cli_SetConnectionParams(S7Object Client, const char *Address, word LocalTSAP, word RemoteTSAP);
EXPORTSPEC int S7API Cli_SetConnectionType(S7Object Client, word ConnectionType);
EXPORTSPEC int S7API Cli_ConnectTo(S7Object Client, const char *Address, int Rack, int Slot);
EXPORTSPEC int S7API Cli_Disconnect(S7Object Client);
EXPORTSPEC int S7API Cli_GetParam(S7Object Client, int ParamNumber, void *pValue);
EXPORTSPEC int S7API Cli_SetParam(S7Object Client, int ParamNumber, void *pValue);
EXPORTSPEC int S7API Cli_SetAsCallback(S7Object Client, pfn_CliCompletion pCompletion, void *usrPtr);
// Data I/O functions
EXPORTSPEC int S7API Cli_ReadArea(S7Object Client, int Area, int DBNumber, int Start, int Amount, int WordLen, void *pUsrData);
EXPORTSPEC int S7API Cli_WriteArea(S7Object Client, int Area, int DBNumber, int Start, int Amount, int WordLen, void *pUsrData);
EXPORTSPEC int S7API Cli_ReadMultiVars(S7Object Client, PS7DataItem Item, int ItemsCount);
EXPORTSPEC int S7API Cli_WriteMultiVars(S7Object Client, PS7DataItem Item, int ItemsCount);
// Data I/O Lean functions
EXPORTSPEC int S7API Cli_DBRead(S7Object Client, int DBNumber, int Start, int Size, void *pUsrData);
EXPORTSPEC int S7API Cli_DBWrite(S7Object Client, int DBNumber, int Start, int Size, void *pUsrData);
EXPORTSPEC int S7API Cli_MBRead(S7Object Client, int Start, int Size, void *pUsrData);
EXPORTSPEC int S7API Cli_MBWrite(S7Object Client, int Start, int Size, void *pUsrData);
EXPORTSPEC int S7API Cli_EBRead(S7Object Client, int Start, int Size, void *pUsrData);
EXPORTSPEC int S7API Cli_EBWrite(S7Object Client, int Start, int Size, void *pUsrData);
EXPORTSPEC int S7API Cli_ABRead(S7Object Client, int Start, int Size, void *pUsrData);
EXPORTSPEC int S7API Cli_ABWrite(S7Object Client, int Start, int Size, void *pUsrData);
EXPORTSPEC int S7API Cli_TMRead(S7Object Client, int Start, int Amount, void *pUsrData);
EXPORTSPEC int S7API Cli_TMWrite(S7Object Client, int Start, int Amount, void *pUsrData);
EXPORTSPEC int S7API Cli_CTRead(S7Object Client, int Start, int Amount, void *pUsrData);
EXPORTSPEC int S7API Cli_CTWrite(S7Object Client, int Start, int Amount, void *pUsrData);
// Directory functions
EXPORTSPEC int S7API Cli_ListBlocks(S7Object Client, TS7BlocksList *pUsrData);
EXPORTSPEC int S7API Cli_GetAgBlockInfo(S7Object Client, int BlockType, int BlockNum, TS7BlockInfo *pUsrData);
EXPORTSPEC int S7API Cli_GetPgBlockInfo(S7Object Client, void *pBlock, TS7BlockInfo *pUsrData, int Size);
EXPORTSPEC int S7API Cli_ListBlocksOfType(S7Object Client, int BlockType, TS7BlocksOfType *pUsrData, int &ItemsCount);
// Blocks functions
EXPORTSPEC int S7API Cli_Upload(S7Object Client, int BlockType, int BlockNum, void *pUsrData, int &Size);
EXPORTSPEC int S7API Cli_FullUpload(S7Object Client, int BlockType, int BlockNum, void *pUsrData, int &Size);
EXPORTSPEC int S7API Cli_Download(S7Object Client, int BlockNum, void *pUsrData, int Size);
EXPORTSPEC int S7API Cli_Delete(S7Object Client, int BlockType, int BlockNum);
EXPORTSPEC int S7API Cli_DBGet(S7Object Client, int DBNumber, void *pUsrData, int &Size);
EXPORTSPEC int S7API Cli_DBFill(S7Object Client, int DBNumber, int FillChar);
// Date/Time functions
EXPORTSPEC int S7API Cli_GetPlcDateTime(S7Object Client, tm &DateTime);
EXPORTSPEC int S7API Cli_SetPlcDateTime(S7Object Client, tm *DateTime);
EXPORTSPEC int S7API Cli_SetPlcSystemDateTime(S7Object Client);
// System Info functions
EXPORTSPEC int S7API Cli_GetOrderCode(S7Object Client, TS7OrderCode *pUsrData);
EXPORTSPEC int S7API Cli_GetCpuInfo(S7Object Client, TS7CpuInfo *pUsrData);
EXPORTSPEC int S7API Cli_GetCpInfo(S7Object Client, TS7CpInfo *pUsrData);
EXPORTSPEC int S7API Cli_ReadSZL(S7Object Client, int ID, int Index, TS7SZL *pUsrData, int &Size);
EXPORTSPEC int S7API Cli_ReadSZLList(S7Object Client, TS7SZLList *pUsrData, int &ItemsCount);
// Control functions
EXPORTSPEC int S7API Cli_PlcHotStart(S7Object Client);
EXPORTSPEC int S7API Cli_PlcColdStart(S7Object Client);
EXPORTSPEC int S7API Cli_PlcStop(S7Object Client);
EXPORTSPEC int S7API Cli_CopyRamToRom(S7Object Client, int Timeout);
EXPORTSPEC int S7API Cli_Compress(S7Object Client, int Timeout);
EXPORTSPEC int S7API Cli_GetPlcStatus(S7Object Client, int &Status);
// Security functions
EXPORTSPEC int S7API Cli_GetProtection(S7Object Client, TS7Protection *pUsrData);
EXPORTSPEC int S7API Cli_SetSessionPassword(S7Object Client, char *Password);
EXPORTSPEC int S7API Cli_ClearSessionPassword(S7Object Client);
// Low level
EXPORTSPEC int S7API Cli_IsoExchangeBuffer(S7Object Client, void *pUsrData, int &Size);
// Misc
EXPORTSPEC int S7API Cli_GetExecTime(S7Object Client, int &Time);
EXPORTSPEC int S7API Cli_GetLastError(S7Object Client, int &LastError);
EXPORTSPEC int S7API Cli_GetPduLength(S7Object Client, int &Requested, int &Negotiated);
EXPORTSPEC int S7API Cli_ErrorText(int Error, char *Text, int TextLen);
EXPORTSPEC int S7API Cli_GetConnected(S7Object Client, int &Connected);
//==============================================================================
// CLIENT EXPORT LIST - Async functions
//==============================================================================
EXPORTSPEC int S7API Cli_AsReadArea(S7Object Client, int Area, int DBNumber, int Start, int Amount, int WordLen, void *pUsrData);
EXPORTSPEC int S7API Cli_AsWriteArea(S7Object Client, int Area, int DBNumber, int Start, int Amount, int WordLen, void *pUsrData);
EXPORTSPEC int S7API Cli_AsDBRead(S7Object Client, int DBNumber, int Start, int Size, void *pUsrData);
EXPORTSPEC int S7API Cli_AsDBWrite(S7Object Client, int DBNumber, int Start, int Size, void *pUsrData);
EXPORTSPEC int S7API Cli_AsMBRead(S7Object Client, int Start, int Size, void *pUsrData);
EXPORTSPEC int S7API Cli_AsMBWrite(S7Object Client, int Start, int Size, void *pUsrData);
EXPORTSPEC int S7API Cli_AsEBRead(S7Object Client, int Start, int Size, void *pUsrData);
EXPORTSPEC int S7API Cli_AsEBWrite(S7Object Client, int Start, int Size, void *pUsrData);
EXPORTSPEC int S7API Cli_AsABRead(S7Object Client, int Start, int Size, void *pUsrData);
EXPORTSPEC int S7API Cli_AsABWrite(S7Object Client, int Start, int Size, void *pUsrData);
EXPORTSPEC int S7API Cli_AsTMRead(S7Object Client, int Start, int Amount, void *pUsrData);
EXPORTSPEC int S7API Cli_AsTMWrite(S7Object Client, int Start, int Amount, void *pUsrData);
EXPORTSPEC int S7API Cli_AsCTRead(S7Object Client, int Start, int Amount, void *pUsrData);
EXPORTSPEC int S7API Cli_AsCTWrite(S7Object Client, int Start, int Amount, void *pUsrData);
EXPORTSPEC int S7API Cli_AsListBlocksOfType(S7Object Client, int BlockType, TS7BlocksOfType *pUsrData, int &ItemsCount);
EXPORTSPEC int S7API Cli_AsReadSZL(S7Object Client, int ID, int Index, TS7SZL *pUsrData, int &Size);
EXPORTSPEC int S7API Cli_AsReadSZLList(S7Object Client, TS7SZLList *pUsrData, int &ItemsCount);
EXPORTSPEC int S7API Cli_AsUpload(S7Object Client, int BlockType, int BlockNum, void *pUsrData, int &Size);
EXPORTSPEC int S7API Cli_AsFullUpload(S7Object Client, int BlockType, int BlockNum, void *pUsrData, int &Size);
EXPORTSPEC int S7API Cli_AsDownload(S7Object Client, int BlockNum, void *pUsrData, int Size);
EXPORTSPEC int S7API Cli_AsCopyRamToRom(S7Object Client, int Timeout);
EXPORTSPEC int S7API Cli_AsCompress(S7Object Client, int Timeout);
EXPORTSPEC int S7API Cli_AsDBGet(S7Object Client, int DBNumber, void *pUsrData, int &Size);
EXPORTSPEC int S7API Cli_AsDBFill(S7Object Client, int DBNumber, int FillChar);
EXPORTSPEC int S7API Cli_CheckAsCompletion(S7Object Client, int &opResult);
EXPORTSPEC int S7API Cli_WaitAsCompletion(S7Object Client, int Timeout);
//==============================================================================
// SERVER EXPORT LIST
//==============================================================================
EXPORTSPEC S7Object S7API Srv_Create();
EXPORTSPEC void S7API Srv_Destroy(S7Object &Server);
EXPORTSPEC int S7API Srv_GetParam(S7Object Server, int ParamNumber, void *pValue);
EXPORTSPEC int S7API Srv_SetParam(S7Object Server, int ParamNumber, void *pValue);
EXPORTSPEC int S7API Srv_Start(S7Object Server);
EXPORTSPEC int S7API Srv_StartTo(S7Object Server, const char *Address);
EXPORTSPEC int S7API Srv_Stop(S7Object Server);
// Data
EXPORTSPEC int S7API Srv_RegisterArea(S7Object Server, int AreaCode, word Index, void *pUsrData, int Size);
EXPORTSPEC int S7API Srv_UnregisterArea(S7Object Server, int AreaCode, word Index);
EXPORTSPEC int S7API Srv_LockArea(S7Object Server, int AreaCode, word Index);
EXPORTSPEC int S7API Srv_UnlockArea(S7Object Server, int AreaCode, word Index);
// Events
EXPORTSPEC int S7API Srv_ClearEvents(S7Object Server);
EXPORTSPEC int S7API Srv_PickEvent(S7Object Server, TSrvEvent *pEvent, int &EvtReady);
EXPORTSPEC int S7API Srv_GetMask(S7Object Server, int MaskKind, longword &Mask);
EXPORTSPEC int S7API Srv_SetMask(S7Object Server, int MaskKind, longword Mask);
EXPORTSPEC int S7API Srv_SetEventsCallback(S7Object Server, pfn_SrvCallBack pCallback, void *usrPtr);
EXPORTSPEC int S7API Srv_SetReadEventsCallback(S7Object Server, pfn_SrvCallBack pCallback, void *usrPtr);
EXPORTSPEC int S7API Srv_EventText(TSrvEvent &Event, char *Text, int TextLen);
EXPORTSPEC int S7API Srv_SetRWAreaCallback(S7Object Server, pfn_RWAreaCallBack pCallback, void *usrPtr);
// Misc
EXPORTSPEC int S7API Srv_GetStatus(S7Object Server, int &ServerStatus, int &CpuStatus, int &ClientsCount);
EXPORTSPEC int S7API Srv_SetCpuStatus(S7Object Server, int CpuStatus);
EXPORTSPEC int S7API Srv_ErrorText(int Error, char *Text, int TextLen);
//==============================================================================
// PARTNER EXPORT LIST
//==============================================================================
EXPORTSPEC S7Object S7API Par_Create(int Active);
EXPORTSPEC void S7API Par_Destroy(S7Object &Partner);
EXPORTSPEC int S7API Par_GetParam(S7Object Partner, int ParamNumber, void *pValue);
EXPORTSPEC int S7API Par_SetParam(S7Object Partner, int ParamNumber, void *pValue);
EXPORTSPEC int S7API Par_Start(S7Object Partner);
EXPORTSPEC int S7API Par_StartTo(S7Object Partner, const char *LocalAddress, const char *RemoteAddress,
word LocTsap, word RemTsap);
EXPORTSPEC int S7API Par_Stop(S7Object Partner);
// BSend
EXPORTSPEC int S7API Par_BSend(S7Object Partner, longword R_ID, void *pUsrData, int Size);
EXPORTSPEC int S7API Par_AsBSend(S7Object Partner, longword R_ID, void *pUsrData, int Size);
EXPORTSPEC int S7API Par_CheckAsBSendCompletion(S7Object Partner, int &opResult);
EXPORTSPEC int S7API Par_WaitAsBSendCompletion(S7Object Partner, longword Timeout);
EXPORTSPEC int S7API Par_SetSendCallback(S7Object Partner, pfn_ParBSendCompletion pCompletion, void *usrPtr);
// BRecv
EXPORTSPEC int S7API Par_BRecv(S7Object Partner, longword &R_ID, void *pData, int &Size, longword Timeout);
EXPORTSPEC int S7API Par_CheckAsBRecvCompletion(S7Object Partner, int &opResult, longword &R_ID,
void *pData, int &Size);
EXPORTSPEC int S7API Par_SetRecvCallback(S7Object Partner, pfn_ParBRecvCallBack pCompletion, void *usrPtr);
// Stat
EXPORTSPEC int S7API Par_GetTimes(S7Object Partner, longword &SendTime, longword &RecvTime);
EXPORTSPEC int S7API Par_GetStats(S7Object Partner, longword &BytesSent, longword &BytesRecv,
longword &SendErrors, longword &RecvErrors);
EXPORTSPEC int S7API Par_GetLastError(S7Object Partner, int &LastError);
EXPORTSPEC int S7API Par_GetStatus(S7Object Partner, int &Status);
EXPORTSPEC int S7API Par_ErrorText(int Error, char *Text, int TextLen);
#endif // snap7_libmain_h

View File

@@ -0,0 +1,923 @@
/*=============================================================================|
| 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 "snap_msgsock.h"
//---------------------------------------------------------------------------
static SocketsLayer SocketsLayerInitializer;
//---------------------------------------------------------------------------
// Base class endian aware
//---------------------------------------------------------------------------
TSnapBase::TSnapBase()
{
int x = 1;
LittleEndian=*(char *)&x == 1;
}
//---------------------------------------------------------------------------
word TSnapBase::SwapWord(word Value)
{
if (LittleEndian)
return ((Value >> 8) & 0xFF) | ((Value << 8) & 0xFF00);
else
return Value;
}
//---------------------------------------------------------------------------
longword TSnapBase::SwapDWord(longword Value)
{
if (LittleEndian)
return (Value >> 24) | ((Value << 8) & 0x00FF0000) | ((Value >> 8) & 0x0000FF00) | (Value << 24);
else
return Value;
}
//---------------------------------------------------------------------------
void Msg_CloseSocket(socket_t FSocket)
{
#ifdef OS_WINDOWS
closesocket(FSocket);
#else
close(FSocket);
#endif
}
//---------------------------------------------------------------------------
longword Msg_GetSockAddr(socket_t FSocket)
{
sockaddr_in RemoteSin;
#ifdef OS_WINDOWS
int namelen = sizeof(RemoteSin);
#else
uint32_t namelen = sizeof(RemoteSin);
#endif
namelen=sizeof(sockaddr_in);
if (getpeername(FSocket,(struct sockaddr*)&RemoteSin, &namelen)==0)
return RemoteSin.sin_addr.s_addr;
else
return 0;
}
//---------------------------------------------------------------------------
TMsgSocket::TMsgSocket()
{
Pinger = new TPinger();
// Set Defaults
strcpy(LocalAddress,"0.0.0.0");
LocalPort=0;
strcpy(RemoteAddress,"127.0.0.1");
RemotePort=0;
WorkInterval=100;
RecvTimeout=500;
SendTimeout=10;
PingTimeout=750;
Connected=false;
FSocket=INVALID_SOCKET;
LastTcpError=0;
LocalBind=0;
}
//---------------------------------------------------------------------------
TMsgSocket::~TMsgSocket()
{
DestroySocket();
delete Pinger;
}
//---------------------------------------------------------------------------
void TMsgSocket::SetSin(sockaddr_in &sin, char *Address, u_short Port)
{
uint32_t in_addr;
in_addr=inet_addr(Address);
memset(&sin, 0, sizeof(sin));
LastTcpError=0;
if (in_addr!=INADDR_NONE)
{
sin.sin_addr.s_addr = in_addr; //INADDR_ANY;
sin.sin_family = AF_INET;
sin.sin_port = htons(Port);
}
else
LastTcpError=WSAEINVALIDADDRESS;
}
//---------------------------------------------------------------------------
void TMsgSocket::GetSin(sockaddr_in sin, char *Address, u_short &Port)
{
strcpy(Address,inet_ntoa(sin.sin_addr));
Port=htons(sin.sin_port);
}
//---------------------------------------------------------------------------
void TMsgSocket::GetLocal()
{
#ifdef OS_WINDOWS
int namelen = sizeof(LocalSin);
#else
uint32_t namelen = sizeof(LocalSin);
#endif
if (getsockname(FSocket, (struct sockaddr*)&LocalSin, &namelen)==0)
GetSin(LocalSin, LocalAddress, LocalPort);
}
//---------------------------------------------------------------------------
void TMsgSocket::GetRemote()
{
#ifdef OS_WINDOWS
int namelen = sizeof(RemoteSin);
#else
uint32_t namelen = sizeof(RemoteSin);
#endif
if (getpeername(FSocket,(struct sockaddr*)&RemoteSin, &namelen)==0)
GetSin(RemoteSin, RemoteAddress, RemotePort);
}
//---------------------------------------------------------------------------
int TMsgSocket::GetLastSocketError()
{
#ifdef OS_WINDOWS
return WSAGetLastError();
#else
return errno;
#endif
}
//---------------------------------------------------------------------------
void TMsgSocket::Purge()
{
// small buffer to empty the socket
char Trash[512];
int Read;
if (LastTcpError!=WSAECONNRESET)
{
if (CanRead(0)) {
do
{
Read=recv(FSocket, Trash, 512, MSG_NOSIGNAL );
} while(Read==512);
}
}
}
//---------------------------------------------------------------------------
void TMsgSocket::CreateSocket()
{
DestroySocket();
LastTcpError=0;
FSocket =socket(AF_INET, SOCK_STREAM, IPPROTO_TCP );
if (FSocket!=INVALID_SOCKET)
SetSocketOptions();
else
LastTcpError =GetLastSocketError();
}
//---------------------------------------------------------------------------
void TMsgSocket::GotSocket()
{
ClientHandle=RemoteSin.sin_addr.s_addr;
// could be inherited it if wee need further actions on the socket
}
//---------------------------------------------------------------------------
void TMsgSocket::SetSocket(socket_t s)
{
FSocket=s;
if (FSocket!=INVALID_SOCKET)
{
SetSocketOptions();
GetLocal();
GetRemote();
GotSocket();
}
Connected=FSocket!=INVALID_SOCKET;
}
//---------------------------------------------------------------------------
void TMsgSocket::DestroySocket()
{
if(FSocket != INVALID_SOCKET)
{
if (shutdown(FSocket, SD_SEND)==0)
Purge();
#ifdef OS_WINDOWS
closesocket(FSocket);
#else
close(FSocket);
#endif
FSocket=INVALID_SOCKET;
}
LastTcpError=0;
}
//---------------------------------------------------------------------------
int TMsgSocket::WaitingData()
{
int result = 0;
u_long x = 0;
#ifdef OS_WINDOWS
if (ioctlsocket(FSocket, FIONREAD, &x) == 0)
result = x;
#else
if (ioctl(FSocket, FIONREAD, &x) == 0)
result = x;
#endif
if (result>MaxPacketSize)
result = MaxPacketSize;
return result;
}
//---------------------------------------------------------------------------
int TMsgSocket::WaitForData(int Size, int Timeout)
{
longword Elapsed;
// Check for connection active
if (CanRead(0) && (WaitingData()==0))
LastTcpError=WSAECONNRESET;
else
LastTcpError=0;
// Enter main loop
if (LastTcpError==0)
{
Elapsed =SysGetTick();
while((WaitingData()<Size) && (LastTcpError==0))
{
// Checks timeout
if (DeltaTime(Elapsed)>=(longword)(Timeout))
LastTcpError =WSAETIMEDOUT;
else
SysSleep(1);
}
}
if(LastTcpError==WSAECONNRESET)
Connected =false;
return LastTcpError;
}
//---------------------------------------------------------------------------
void TMsgSocket::SetSocketOptions()
{
int NoDelay = 1;
int KeepAlive = 1;
LastTcpError=0;
SockCheck(setsockopt(FSocket, IPPROTO_TCP, TCP_NODELAY,(char*)&NoDelay, sizeof(NoDelay)));
if (LastTcpError==0)
SockCheck(setsockopt(FSocket, SOL_SOCKET, SO_KEEPALIVE,(char*)&KeepAlive, sizeof(KeepAlive)));
}
//---------------------------------------------------------------------------
int TMsgSocket::SockCheck(int SockResult)
{
if (SockResult == (int)(SOCKET_ERROR))
LastTcpError = GetLastSocketError();
return LastTcpError;
}
//---------------------------------------------------------------------------
bool TMsgSocket::CanWrite(int Timeout)
{
timeval TimeV;
int64_t x;
fd_set FDset;
if(FSocket == INVALID_SOCKET)
return false;
TimeV.tv_usec = (Timeout % 1000) * 1000;
TimeV.tv_sec = Timeout / 1000;
FD_ZERO(&FDset);
FD_SET(FSocket, &FDset);
x = select(FSocket + 1, NULL, &FDset, NULL, &TimeV); //<-Ignore this warning in 64bit Visual Studio
if (x==(int)SOCKET_ERROR)
{
LastTcpError = GetLastSocketError();
x=0;
}
return (x > 0);
}
//---------------------------------------------------------------------------
bool TMsgSocket::CanRead(int Timeout)
{
timeval TimeV;
int64_t x;
fd_set FDset;
if(FSocket == INVALID_SOCKET)
return false;
TimeV.tv_usec = (Timeout % 1000) * 1000;
TimeV.tv_sec = Timeout / 1000;
FD_ZERO(&FDset);
FD_SET(FSocket, &FDset);
x = select(FSocket + 1, &FDset, NULL, NULL, &TimeV); //<-Ignore this warning in 64bit Visual Studio
if (x==(int)SOCKET_ERROR)
{
LastTcpError = GetLastSocketError();
x=0;
}
return (x > 0);
}
//---------------------------------------------------------------------------
#ifdef NON_BLOCKING_CONNECT
//
// Non blocking connection (UNIX) Thanks to Rolf Stalder
//
int TMsgSocket::SckConnect()
{
int n, flags, err;
socklen_t len;
fd_set rset, wset;
struct timeval tval;
SetSin(RemoteSin, RemoteAddress, RemotePort);
if (LastTcpError == 0) {
CreateSocket();
if (LastTcpError == 0) {
flags = fcntl(FSocket, F_GETFL, 0);
if (flags >= 0) {
if (fcntl(FSocket, F_SETFL, flags | O_NONBLOCK) != -1) {
n = connect(FSocket, (struct sockaddr*)&RemoteSin, sizeof(RemoteSin));
if (n < 0) {
if (errno != EINPROGRESS) {
LastTcpError = GetLastSocketError();
}
else {
// still connecting ...
FD_ZERO(&rset);
FD_SET(FSocket, &rset);
wset = rset;
tval.tv_sec = PingTimeout / 1000;
tval.tv_usec = (PingTimeout % 1000) * 1000;
n = select(FSocket+1, &rset, &wset, NULL,
(PingTimeout ? &tval : NULL));
if (n == 0) {
// timeout
LastTcpError = WSAEHOSTUNREACH;
}
else {
if (FD_ISSET(FSocket, &rset) || FD_ISSET(FSocket, &wset)) {
err = 0;
len = sizeof(err);
if (getsockopt(
FSocket, SOL_SOCKET, SO_ERROR, &err, &len) == 0) {
if (err) {
LastTcpError = err;
}
else {
if (fcntl(FSocket, F_SETFL, flags) != -1) {
GetLocal();
ClientHandle = LocalSin.sin_addr.s_addr;
}
else {
LastTcpError = GetLastSocketError();
}
}
}
else {
LastTcpError = GetLastSocketError();
}
}
else {
LastTcpError = -1;
}
}
} // still connecting
}
else if (n == 0) {
// connected immediatly
GetLocal();
ClientHandle = LocalSin.sin_addr.s_addr;
}
}
else {
LastTcpError = GetLastSocketError();
} // fcntl(F_SETFL)
}
else {
LastTcpError = GetLastSocketError();
} // fcntl(F_GETFL)
} //valid socket
} // LastTcpError==0
Connected=LastTcpError==0;
return LastTcpError;
}
#else
//
// Regular connection (Windows)
//
int TMsgSocket::SckConnect()
{
int Result;
SetSin(RemoteSin, RemoteAddress, RemotePort);
if (LastTcpError==0)
{
if (Ping(RemoteSin))
{
CreateSocket();
if (LastTcpError==0)
{
Result=connect(FSocket, (struct sockaddr*)&RemoteSin, sizeof(RemoteSin));
if (SockCheck(Result)==0)
{
GetLocal();
// Client handle is self_address (here the connection is ACTIVE)
ClientHandle=LocalSin.sin_addr.s_addr;
}
}
}
else
LastTcpError=WSAEHOSTUNREACH;
}
Connected=LastTcpError==0;
return LastTcpError;
}
#endif
//---------------------------------------------------------------------------
void TMsgSocket::SckDisconnect()
{
DestroySocket();
Connected=false;
}
//---------------------------------------------------------------------------
void TMsgSocket::ForceClose()
{
if(FSocket != INVALID_SOCKET)
{
try {
#ifdef OS_WINDOWS
closesocket(FSocket);
#else
close(FSocket);
#endif
} catch (...) {
}
FSocket=INVALID_SOCKET;
}
LastTcpError=0;
}
//---------------------------------------------------------------------------
int TMsgSocket::SckBind()
{
int Res;
int Opt=1;
SetSin(LocalSin, LocalAddress, LocalPort);
if (LastTcpError==0)
{
CreateSocket();
if (LastTcpError==0)
{
setsockopt(FSocket ,SOL_SOCKET, SO_REUSEADDR, (const char *)&Opt, sizeof(int));
Res =bind(FSocket, (struct sockaddr*)&LocalSin, sizeof(sockaddr_in));
SockCheck(Res);
if (Res==0)
{
LocalBind=LocalSin.sin_addr.s_addr;
}
}
}
else
LastTcpError=WSAEINVALIDADDRESS;
return LastTcpError;
}
//---------------------------------------------------------------------------
int TMsgSocket::SckListen()
{
LastTcpError=0;
SockCheck(listen(FSocket ,SOMAXCONN));
return LastTcpError;
}
//---------------------------------------------------------------------------
bool TMsgSocket::Ping(char *Host)
{
return Pinger->Ping(Host, PingTimeout);
}
//---------------------------------------------------------------------------
bool TMsgSocket::Ping(sockaddr_in Addr)
{
if (PingTimeout == 0)
return true;
else
return Pinger->Ping(Addr.sin_addr.s_addr, PingTimeout);
}
//---------------------------------------------------------------------------
socket_t TMsgSocket::SckAccept()
{
socket_t result;
LastTcpError=0;
result = accept(FSocket, NULL, NULL);
if(result==INVALID_SOCKET)
LastTcpError =GetLastSocketError();
return result;
}
//---------------------------------------------------------------------------
int TMsgSocket::SendPacket(void *Data, int Size)
{
int Result;
LastTcpError=0;
if (SendTimeout>0)
{
if (!CanWrite(SendTimeout))
{
LastTcpError = WSAETIMEDOUT;
return LastTcpError;
}
}
if (send(FSocket, (char*)Data, Size, MSG_NOSIGNAL)==Size)
return 0;
else
Result =SOCKET_ERROR;
SockCheck(Result);
return Result;
}
//---------------------------------------------------------------------------
bool TMsgSocket::PacketReady(int Size)
{
return (WaitingData()>=Size);
}
//---------------------------------------------------------------------------
int TMsgSocket::Receive(void *Data, int BufSize, int &SizeRecvd)
{
LastTcpError=0;
if (CanRead(RecvTimeout))
{
SizeRecvd=recv(FSocket ,(char*)Data ,BufSize ,MSG_NOSIGNAL );
if (SizeRecvd>0) // something read (default case)
LastTcpError=0;
else
if (SizeRecvd==0)
LastTcpError = WSAECONNRESET; // Connection reset by Peer
else
LastTcpError=GetLastSocketError(); // we need to know what happened
}
else
LastTcpError = WSAETIMEDOUT;
if (LastTcpError==WSAECONNRESET)
Connected = false;
return LastTcpError;
}
//---------------------------------------------------------------------------
int TMsgSocket::RecvPacket(void *Data, int Size)
{
int BytesRead;
WaitForData(Size, RecvTimeout);
if (LastTcpError==0)
{
BytesRead=recv(FSocket, (char*)Data, Size, MSG_NOSIGNAL);
if (BytesRead==0)
LastTcpError = WSAECONNRESET; // Connection reset by Peer
else
if (BytesRead<0)
LastTcpError = GetLastSocketError();
}
else // After the timeout the bytes waiting were less then we expected
if (LastTcpError==WSAETIMEDOUT)
Purge();
if (LastTcpError==WSAECONNRESET)
Connected =false;
return LastTcpError;
}
//---------------------------------------------------------------------------
int TMsgSocket::PeekPacket(void *Data, int Size)
{
int BytesRead;
WaitForData(Size, RecvTimeout);
if (LastTcpError==0)
{
BytesRead=recv(FSocket, (char*)Data, Size, MSG_PEEK | MSG_NOSIGNAL );
if (BytesRead==0)
LastTcpError = WSAECONNRESET; // Connection reset by Peer
else
if (BytesRead<0)
LastTcpError = GetLastSocketError();
}
else // After the timeout the bytes waiting were less then we expected
if (LastTcpError==WSAETIMEDOUT)
Purge();
if (LastTcpError==WSAECONNRESET)
Connected =false;
return LastTcpError;
}
//---------------------------------------------------------------------------
bool TMsgSocket::Execute()
{
return true;
}
//==============================================================================
// PING
//==============================================================================
static int PingKind;
#ifdef OS_WINDOWS
// iphlpapi, is loaded dinamically because if this fails we can still try
// to use raw sockets
static char const *iphlpapi = "\\iphlpapi.dll";
#pragma pack(1)
//typedef byte TTxBuffer[40];
typedef byte TTxBuffer[32];
#pragma pack()
typedef HANDLE (__stdcall *pfn_IcmpCreateFile)();
typedef bool (__stdcall *pfn_IcmpCloseHandle)(HANDLE PingHandle);
typedef int (__stdcall *pfn_IcmpSendEcho2)(
HANDLE PingHandle,
void *Event,
void *AcpRoutine,
void *AcpContext,
unsigned long DestinationAddress,
void *RequestData,
int RequestSize,
void *not_used, //should be *IP_OPTION_INFORMATION but we don't use it
void *ReplyBuffer,
int ReplySize,
int Timeout
);
static pfn_IcmpCreateFile IcmpCreateFile;
static pfn_IcmpCloseHandle IcmpCloseHandle;
static pfn_IcmpSendEcho2 IcmpSendEcho2;
static HINSTANCE IcmpDllHandle = 0;
static bool IcmpAvail = false;
bool IcmpInit()
{
char iphlppath[MAX_PATH+12];
int PathLen = GetSystemDirectoryA(iphlppath, MAX_PATH);
if (PathLen != 0)
{
strcat(iphlppath, iphlpapi);
IcmpDllHandle = LoadLibraryA(iphlppath);
}
else
IcmpDllHandle = 0;
if (IcmpDllHandle != 0)
{
IcmpCreateFile=(pfn_IcmpCreateFile)GetProcAddress(IcmpDllHandle,"IcmpCreateFile");
IcmpCloseHandle=(pfn_IcmpCloseHandle)GetProcAddress(IcmpDllHandle,"IcmpCloseHandle");
IcmpSendEcho2=(pfn_IcmpSendEcho2)GetProcAddress(IcmpDllHandle,"IcmpSendEcho2");
return (IcmpCreateFile!=NULL) && (IcmpCloseHandle!=NULL) && (IcmpSendEcho2!=NULL);
}
else
return false;
}
void IcmpDone()
{
if (IcmpDllHandle!=0)
FreeLibrary(IcmpDllHandle);
IcmpAvail=false;
}
#endif
//---------------------------------------------------------------------------
// RAW Socket Pinger
//---------------------------------------------------------------------------
TRawSocketPinger::TRawSocketPinger()
{
FSocket =socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
FId =word(size_t(this));
FSeq =0;
}
//---------------------------------------------------------------------------
TRawSocketPinger::~TRawSocketPinger()
{
if (FSocket!=INVALID_SOCKET)
{
#ifdef OS_WINDOWS
closesocket(FSocket);
#else
close(FSocket);
#endif
FSocket=INVALID_SOCKET;
};
}
//---------------------------------------------------------------------------
void TRawSocketPinger::InitPacket()
{
memset(&IcmpBuffer,0,ICmpBufferSize);
FSeq++;
SendPacket=PIcmpPacket(pbyte(&IcmpBuffer)+sizeof(TIPHeader));
SendPacket->Header.ic_type=ICMP_ECHORQ;
SendPacket->Header.ic_code=0;
SendPacket->Header.ic_cksum=0;
SendPacket->Header.ic_id=FId;
SendPacket->Header.ic_seq=FSeq;
memset(&SendPacket->Data,0,sizeof(SendPacket->Data));
SendPacket->Header.ic_cksum=PacketChecksum();
}
//---------------------------------------------------------------------------
word TRawSocketPinger::PacketChecksum()
{
word *P = (word*)(SendPacket);
longword Sum = 0;
int c;
for (c = 0; c < int(sizeof(TIcmpPacket) / 2); c++) {
Sum+=*P;
P++;
}
Sum=(Sum >> 16) + (Sum & 0xFFFF);
Sum=Sum+(Sum >> 16);
return word(~Sum);
}
//---------------------------------------------------------------------------
bool TRawSocketPinger::CanRead(int Timeout)
{
timeval TimeV;
int64_t x;
fd_set FDset;
TimeV.tv_usec = (Timeout % 1000) * 1000;
TimeV.tv_sec = Timeout / 1000;
FD_ZERO(&FDset);
FD_SET(FSocket, &FDset);
x = select(FSocket + 1, &FDset, NULL, NULL, &TimeV); //<-Ignore this warning in 64bit Visual Studio
if (x==(int)(SOCKET_ERROR))
x=0;
return (x > 0);
}
//---------------------------------------------------------------------------
bool TRawSocketPinger::Ping(longword ip_addr, int Timeout)
{
sockaddr_in LSockAddr;
sockaddr_in RSockAddr;
PIcmpReply Reply;
if (FSocket==INVALID_SOCKET)
return true;
// Init packet
InitPacket();
Reply=PIcmpReply(&IcmpBuffer);
// Init Remote and Local Addresses struct
RSockAddr.sin_family=AF_INET;
RSockAddr.sin_port=0;
RSockAddr.sin_addr.s_addr=ip_addr;
LSockAddr.sin_family=AF_INET;
LSockAddr.sin_port=0;
LSockAddr.sin_addr.s_addr=inet_addr("0.0.0.0");
// Bind to local
if (bind(FSocket, (struct sockaddr*)&LSockAddr, sizeof(sockaddr_in))!=0)
return false;
// Connect to remote (not a really TCP connection, only to setup the socket)
if (connect(FSocket, (struct sockaddr*)&RSockAddr, sizeof(sockaddr_in))!=0)
return false;
// Send ICMP packet
if (send(FSocket, (char*)SendPacket, sizeof(TIcmpPacket), MSG_NOSIGNAL)!=int(sizeof(TIcmpPacket)))
return false;
// Wait for a reply
if (!CanRead(Timeout))
return false;// time expired
// Get the answer
if (recv(FSocket, (char*)&IcmpBuffer, ICmpBufferSize, MSG_NOSIGNAL)<int(sizeof(TIcmpReply)))
return false;
// Check the answer
return (Reply->IPH.ip_src==RSockAddr.sin_addr.s_addr) && // the peer is what we are looking for
(Reply->ICmpReply.Header.ic_type==ICMP_ECHORP); // type = reply
}
//---------------------------------------------------------------------------
// Pinger
//---------------------------------------------------------------------------
TPinger::TPinger()
{
}
//---------------------------------------------------------------------------
TPinger::~TPinger()
{
}
//---------------------------------------------------------------------------
bool TPinger::RawPing(longword ip_addr, int Timeout)
{
PRawSocketPinger RawPinger = new TRawSocketPinger();
bool Result;
Result=RawPinger->Ping(ip_addr, Timeout);
delete RawPinger;
return Result;
}
//---------------------------------------------------------------------------
#ifdef OS_WINDOWS
bool TPinger::WinPing(longword ip_addr, int Timeout)
{
HANDLE PingHandle;
TTxBuffer TxBuffer;
TIcmpBuffer IcmpBuffer;
bool Result;
PingHandle = IcmpCreateFile();
if (PingHandle != INVALID_HANDLE_VALUE)
{
memset(&TxBuffer,'\55',sizeof(TTxBuffer));
Result=(IcmpSendEcho2(PingHandle, NULL, NULL, NULL, ip_addr,
&TxBuffer, sizeof(TxBuffer), NULL, &IcmpBuffer, ICmpBufferSize, Timeout))>0;
IcmpCloseHandle(PingHandle);
return Result;
}
else
return false;
}
#endif
//---------------------------------------------------------------------------
bool TPinger::Ping(char *Host, int Timeout)
{
longword Addr;
Addr=inet_addr(Host);
return Ping(Addr, Timeout);
}
//---------------------------------------------------------------------------
bool TPinger::Ping(longword ip_addr, int Timeout)
{
#ifdef OS_WINDOWS
if (PingKind==pkWinHelper)
return WinPing(ip_addr, Timeout);
else
#endif
if (PingKind==pkRawSocket)
return RawPing(ip_addr, Timeout);
else
return true; // we still need to continue
}
//---------------------------------------------------------------------------
// Checks if raw sockets are allowed
//---------------------------------------------------------------------------
bool RawSocketsCheck()
{
socket_t RawSocket;
bool Result;
RawSocket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
Result=RawSocket != INVALID_SOCKET;
if (Result)
#ifdef OS_WINDOWS
closesocket(RawSocket);
#else
close(RawSocket);
#endif
return Result;
}
//---------------------------------------------------------------------------
// Sockets init
// - Winsock Startup (Windows)
// - ICMP Helper Load (Windows)
// - Check for raw socket (Unix or Windows if ICMP load failed)
//---------------------------------------------------------------------------
SocketsLayer::SocketsLayer()
{
#ifdef OS_WINDOWS
timeBeginPeriod(1); // it's not strictly related to socket but here is a nice place
WSAStartup(0x202,&wsaData);
if (IcmpInit())
PingKind=pkWinHelper;
else
#endif
if (RawSocketsCheck())
PingKind=pkRawSocket;
else
PingKind=pkCannotPing;
}
SocketsLayer::~SocketsLayer()
{
#ifdef OS_WINDOWS
IcmpDone();
WSACleanup();
timeEndPeriod(1);
#endif
}

View File

@@ -0,0 +1,339 @@
/*=============================================================================|
| 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 snap_msgsock_h
#define snap_msgsock_h
//---------------------------------------------------------------------------
#include "snap_platform.h"
#include "snap_sysutils.h"
//----------------------------------------------------------------------------
#if defined(OS_WINDOWS) || defined (OS_SOLARIS) || defined(OS_OSX)
# define MSG_NOSIGNAL 0
#endif
//----------------------------------------------------------------------------
// Non blocking connection to avoid root priviledges under UNIX
// i.e. raw socket pinger is not more used.
// Thanks to Rolf Stalder that made it ;)
//----------------------------------------------------------------------------
#ifdef PLATFORM_UNIX
#define NON_BLOCKING_CONNECT
#endif
#ifdef NON_BLOCKING_CONNECT
#include <fcntl.h>
#endif
//----------------------------------------------------------------------------
/*
In Windows sizeof socket varies depending of the platform :
win32 -> sizeof(SOCKET) = 4
win64 -> sizeof(SOCKET) = 8
Even though sizeof(SOCKET) is 8, should be safe to cast it to int, because
the value constitutes an index in per-process table of limited size
and not a real pointer.
Other Os define the socket as int regardless of the processor.
We want to sleep peacefully, so it's better to define a portable socket.
*/
#ifdef OS_WINDOWS
typedef SOCKET socket_t;
#else
typedef int socket_t;
#endif
//----------------------------------------------------------------------------
#define SD_RECEIVE 0x00
#define SD_SEND 0x01
#define SD_BOTH 0x02
#define MaxPacketSize 65536
//----------------------------------------------------------------------------
// For other platform we need to re-define next constants
#if defined(PLATFORM_UNIX) || defined(OS_OSX)
#define INVALID_SOCKET (socket_t)(~0)
#define SOCKET_ERROR (-1)
#define WSAEINTR EINTR
#define WSAEBADF EBADF
#define WSAEACCES EACCES
#define WSAEFAULT EFAULT
#define WSAEINVAL EINVAL
#define WSAEMFILE EMFILE
#define WSAEWOULDBLOCK EWOULDBLOCK
#define WSAEINPROGRESS EINPROGRESS
#define WSAEALREADY EALREADY
#define WSAENOTSOCK ENOTSOCK
#define WSAEDESTADDRREQ EDESTADDRREQ
#define WSAEMSGSIZE EMSGSIZE
#define WSAEPROTOTYPE EPROTOTYPE
#define WSAENOPROTOOPT ENOPROTOOPT
#define WSAEPROTONOSUPPORT EPROTONOSUPPORT
#define WSAESOCKTNOSUPPORT ESOCKTNOSUPPORT
#define WSAEOPNOTSUPP EOPNOTSUPP
#define WSAEPFNOSUPPORT EPFNOSUPPORT
#define WSAEAFNOSUPPORT EAFNOSUPPORT
#define WSAEADDRINUSE EADDRINUSE
#define WSAEADDRNOTAVAIL EADDRNOTAVAIL
#define WSAENETDOWN ENETDOWN
#define WSAENETUNREACH ENETUNREACH
#define WSAENETRESET ENETRESET
#define WSAECONNABORTED ECONNABORTED
#define WSAECONNRESET ECONNRESET
#define WSAENOBUFS ENOBUFS
#define WSAEISCONN EISCONN
#define WSAENOTCONN ENOTCONN
#define WSAESHUTDOWN ESHUTDOWN
#define WSAETOOMANYREFS ETOOMANYREFS
#define WSAETIMEDOUT ETIMEDOUT
#define WSAECONNREFUSED ECONNREFUSED
#define WSAELOOP ELOOP
#define WSAENAMETOOLONG ENAMETOOLONG
#define WSAEHOSTDOWN EHOSTDOWN
#define WSAEHOSTUNREACH EHOSTUNREACH
#define WSAENOTEMPTY ENOTEMPTY
#define WSAEUSERS EUSERS
#define WSAEDQUOT EDQUOT
#define WSAESTALE ESTALE
#define WSAEREMOTE EREMOTE
#endif
#define WSAEINVALIDADDRESS 12001
#define ICmpBufferSize 4096
typedef byte TIcmpBuffer[ICmpBufferSize];
// Ping result
#define PR_CANNOT_PERFORM -1 // cannot ping :
// unix : no root rights or SUID flag set to
// open raw sockets
// windows : neither helper DLL found nor raw
// sockets can be opened (no administrator rights)
// In this case the execution continues whitout
// the benefit of the smart-connect.
#define PR_SUCCESS 0 // Host found
#define PR_ERROR 1 // Ping Error, Ping was performed but ...
// - host didn't replied (not found)
// - routing error
// - TTL expired
// - ... all other icmp error that we don't need
// to know.
// Ping Kind
#define pkCannotPing 1 // see PR_CANNOT_PERFORM comments
#define pkWinHelper 2 // use iphlpapi.dll (only windows)
#define pkRawSocket 3 // use raw sockets (unix/windows)
const byte ICMP_ECHORP = 0; // ECHO Reply
const byte ICMP_ECHORQ = 8; // ECHO Request
//---------------------------------------------------------------------------
// RAW SOCKET PING STRUCTS
//---------------------------------------------------------------------------
#pragma pack(1)
typedef struct{
byte ip_hl_v;
byte ip_tos;
word ip_len;
word ip_id ;
word ip_off;
byte ip_ttl;
byte ip_p;
word ip_sum;
longword ip_src;
longword ip_dst;
}TIPHeader;
typedef struct{
byte ic_type; // Type of message
byte ic_code; // Code
word ic_cksum; // 16 bit checksum
word ic_id; // ID (ic1 : ipv4)
word ic_seq; // Sequence
}TIcmpHeader;
typedef struct{
TIcmpHeader Header;
byte Data[32]; // use the well known default
}TIcmpPacket, *PIcmpPacket;
typedef struct{
TIPHeader IPH;
TIcmpPacket ICmpReply;
}TIcmpReply, *PIcmpReply;
#pragma pack()
//---------------------------------------------------------------------------
class TRawSocketPinger
{
private:
socket_t FSocket;
PIcmpPacket SendPacket;
TIcmpBuffer IcmpBuffer;
word FId, FSeq;
void InitPacket();
word PacketChecksum();
bool CanRead(int Timeout);
public:
bool Ping(longword ip_addr, int Timeout);
TRawSocketPinger();
~TRawSocketPinger();
};
typedef TRawSocketPinger *PRawSocketPinger;
//---------------------------------------------------------------------------
class TPinger
{
private:
PRawSocketPinger RawPinger;
bool RawAvail;
#ifdef OS_WINDOWS
bool WinPing(longword ip_addr, int Timeout);
#endif
bool RawPing(longword ip_addr, int Timeout);
public:
TPinger();
~TPinger();
bool Ping(char *Host, int Timeout);
bool Ping(longword ip_addr, int Timeout);
};
typedef TPinger *PPinger;
//---------------------------------------------------------------------------
class TSnapBase // base class endian-aware
{
private:
bool LittleEndian;
protected:
longword SwapDWord(longword Value);
word SwapWord(word Value);
public:
TSnapBase();
};
//---------------------------------------------------------------------------
class TMsgSocket : public TSnapBase
{
private:
PPinger Pinger;
int GetLastSocketError();
int SockCheck(int SockResult);
void DestroySocket();
void SetSocketOptions();
bool CanWrite(int Timeout);
void GetLocal();
void GetRemote();
void SetSin(sockaddr_in &sin, char *Address, u_short Port);
void GetSin(sockaddr_in sin, char *Address, u_short &Port);
protected:
socket_t FSocket;
sockaddr_in LocalSin;
sockaddr_in RemoteSin;
//--------------------------------------------------------------------------
// low level socket
void CreateSocket();
// Called when a socket is assigned externally
void GotSocket();
// Returns how many bytes are ready to be read in the winsock buffer
int WaitingData();
// Waits until there at least "size" bytes ready to be read or until receive timeout occurs
int WaitForData(int Size, int Timeout);
// Clear socket input buffer
void Purge();
public:
longword ClientHandle;
longword LocalBind;
// Coordinates Address:Port
char LocalAddress[16];
char RemoteAddress[16];
word LocalPort;
word RemotePort;
// "speed" of the socket listener (used server-side)
int WorkInterval;
// Timeouts : 3 different values for fine tuning.
// Send timeout should be small since with work with small packets and TCP_NO_DELAY
// option, so we don't expect "time to wait".
// Recv timeout depends of equipment's processing time : we send a packet, the equipment
// processes the message, finally it sends the answer. In any case Recv timeout > Send Timeout.
// PingTimeout is the maximum time interval during which we expect that the PLC answers.
// By default is 750 ms, increase it if there are many switch/repeaters.
int PingTimeout;
int RecvTimeout;
int SendTimeout;
//int ConnTimeout;
// Output : Last operation error
int LastTcpError;
// Output : Connected to the remote Host/Peer/Client
bool Connected;
//--------------------------------------------------------------------------
TMsgSocket();
virtual ~TMsgSocket();
// Returns true if "something" can be read during the Timeout interval..
bool CanRead(int Timeout);
// Connects to a peer (using RemoteAddress and RemotePort)
int SckConnect(); // (client-side)
// Disconnects from a peer (gracefully)
void SckDisconnect();
// Disconnects RAW
void ForceClose();
// Binds to a local adapter (using LocalAddress and LocalPort) (server-side)
int SckBind();
// Listens for an incoming connection (server-side)
int SckListen();
// Set an external socket reference (tipically from a listener)
void SetSocket(socket_t s);
// Accepts an incoming connection returning a socket descriptor (server-side)
socket_t SckAccept();
// Pings the peer before connecting
bool Ping(char *Host);
bool Ping(sockaddr_in Addr);
// Sends a packet
int SendPacket(void *Data, int Size);
// Returns true if a Packet at least of "Size" bytes is ready to be read
bool PacketReady(int Size);
// Receives everything
int Receive(void *Data, int BufSize, int & SizeRecvd);
// Receives a packet of size specified.
int RecvPacket(void *Data, int Size);
// Peeks a packet of size specified without extract it from the socket queue
int PeekPacket(void *Data, int Size);
virtual bool Execute();
};
typedef TMsgSocket *PMsgSocket;
//---------------------------------------------------------------------------
void Msg_CloseSocket(socket_t FSocket);
longword Msg_GetSockAddr(socket_t FSocket);
//---------------------------------------------------------------------------
class SocketsLayer
{
private:
#ifdef OS_WINDOWS
WSADATA wsaData;
#endif
public:
SocketsLayer();
~SocketsLayer();
};
#endif // snap_msgsock_h

View File

@@ -0,0 +1,152 @@
/*=============================================================================|
| PROJECT SNAP7 1.4.1 |
|==============================================================================|
| 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 snap_platform_h
#define snap_platform_h
//---------------------------------------------------------------------------
#if defined (_WIN32)|| defined(_WIN64)|| defined(__WIN32__) || defined(__WINDOWS__)
# define OS_WINDOWS
#endif
// Visual Studio needs this to use the correct time_t size
#if defined (_WIN32) && !defined(_WIN64) && !defined(_EMBEDDING_VS2013UP)
# define _USE_32BIT_TIME_T
#endif
// Linux, BSD and Solaris define "unix", OSX doesn't, even though it derives from BSD
#if defined(unix) || defined(__unix__) || defined(__unix)
# define PLATFORM_UNIX
#endif
#if BSD>=0
# define OS_BSD
#endif
#if __APPLE__
# define OS_OSX
#endif
#if defined(__SVR4) || defined(__svr4__)
# define OS_SOLARIS
// Thanks to Rolf Stalder now it's possible to use pthreads also for Solaris
// In any case the Solaris native threads model is still present and can be
// used uncommenting the #define line below.
# undef OS_SOLARIS_NATIVE_THREADS
// # define OS_SOLARIS_NATIVE_THREADS
#endif
#if defined(PLATFORM_UNIX)
# include <unistd.h>
# include <sys/param.h>
# if defined(_POSIX_VERSION)
# define POSIX
# endif
#endif
#ifdef OS_OSX
# include <unistd.h>
#endif
#if (!defined (OS_WINDOWS)) && (!defined(PLATFORM_UNIX)) && (!defined(OS_BSD)) && (!defined(OS_OSX))
# error platform still unsupported (please add it yourself and report ;-)
#endif
// Visual C++ not C99 compliant (VS2008--)
#ifdef _MSC_VER
# if _MSC_VER >= 1600
# include <stdint.h> // VS2010++ have it
# else
typedef signed __int8 int8_t;
typedef signed __int16 int16_t;
typedef signed __int32 int32_t;
typedef signed __int64 int64_t;
typedef unsigned __int8 uint8_t;
typedef unsigned __int16 uint16_t;
typedef unsigned __int32 uint32_t;
typedef unsigned __int64 uint64_t;
#ifdef _WIN64
typedef unsigned __int64 uintptr_t;
#else
typedef unsigned __int32 uintptr_t;
#endif
# endif
#else
# include <stdint.h>
#endif
#include <time.h>
#include <cstring>
#include <stdlib.h>
#ifdef OS_WINDOWS
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
# include <winsock2.h>
# include <mmsystem.h>
#endif
#ifdef OS_SOLARIS
# include <sys/filio.h>
# include <cstdlib>
# include <string.h>
#endif
#if defined(PLATFORM_UNIX) || defined(OS_OSX)
# include <errno.h>
# include <sys/time.h>
# include <sys/socket.h>
# include <arpa/inet.h>
# include <netinet/tcp.h>
# include <netinet/in.h>
# include <sys/ioctl.h>
#endif
#ifdef OS_WINDOWS
# define EXPORTSPEC extern "C" __declspec ( dllexport )
# define S7API __stdcall
#else
# define EXPORTSPEC extern "C"
# define S7API
#endif
// Exact length types regardless of platform/processor
// We absolute need of them, all structs have an exact size that
// must be the same across the processor used 32/64 bit
// *Use them* if you change/expand the code and avoid long, u_long and so on...
typedef uint8_t byte;
typedef uint16_t word;
typedef uint32_t longword;
typedef byte *pbyte;
typedef word *pword;
typedef uintptr_t snap_obj; // multi platform/processor object reference
#ifndef OS_WINDOWS
# define INFINITE 0XFFFFFFFF
#endif
#endif // snap_platform_h

View File

@@ -0,0 +1,73 @@
/*=============================================================================|
| 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 "snap_sysutils.h"
#ifdef OS_OSX
int clock_gettime(int clk_id, struct timespec* t)
{
struct timeval now;
int rv = gettimeofday(&now, NULL);
if (rv) return rv;
t->tv_sec = now.tv_sec;
t->tv_nsec = now.tv_usec * 1000;
return 0;
}
#endif
//---------------------------------------------------------------------------
longword SysGetTick()
{
#ifdef OS_WINDOWS
return timeGetTime();
#else
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (longword) (ts.tv_sec * 1000) + (ts.tv_nsec / 1000000);
#endif
}
//---------------------------------------------------------------------------
void SysSleep(longword Delay_ms)
{
#ifdef OS_WINDOWS
Sleep(Delay_ms);
#else
struct timespec ts;
ts.tv_sec = (time_t)(Delay_ms / 1000);
ts.tv_nsec =(long)((Delay_ms - ts.tv_sec) * 1000000);
nanosleep(&ts, (struct timespec *)0);
#endif
}
//---------------------------------------------------------------------------
longword DeltaTime(longword &Elapsed)
{
longword TheTime;
TheTime=SysGetTick();
// Checks for rollover
if (TheTime<Elapsed)
Elapsed=0;
return TheTime-Elapsed;
}

View File

@@ -0,0 +1,39 @@
/*=============================================================================|
| 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 snap_sysutils_h
#define snap_sysutils_h
//---------------------------------------------------------------------------
#include "snap_platform.h"
//---------------------------------------------------------------------------
#ifdef OS_OSX
# define CLOCK_MONOTONIC 0
#endif
longword SysGetTick();
void SysSleep(longword Delay_ms);
longword DeltaTime(longword &Elapsed);
#endif // snap_sysutils_h

View File

@@ -0,0 +1,487 @@
/*=============================================================================|
| 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 "snap_tcpsrvr.h"
//---------------------------------------------------------------------------
// EVENTS QUEUE
//---------------------------------------------------------------------------
TMsgEventQueue::TMsgEventQueue(const int Capacity, const int BlockSize)
{
FCapacity = Capacity;
Max = FCapacity - 1;
FBlockSize = BlockSize;
Buffer = new byte[FCapacity * FBlockSize];
Flush();
}
//---------------------------------------------------------------------------
TMsgEventQueue::~TMsgEventQueue()
{
delete[] Buffer;
}
//---------------------------------------------------------------------------
void TMsgEventQueue::Flush()
{
IndexIn = 0;
IndexOut = 0;
}
//---------------------------------------------------------------------------
void TMsgEventQueue::Insert(void *lpdata)
{
pbyte PBlock;
if (!Full())
{
// Calc offset
if (IndexIn < Max) IndexIn++;
else IndexIn = 0;
PBlock = Buffer + uintptr_t(IndexIn * FBlockSize);
memcpy(PBlock, lpdata, FBlockSize);
};
}
//---------------------------------------------------------------------------
bool TMsgEventQueue::Extract(void *lpdata)
{
int IdxOut;
pbyte PBlock;
if (!Empty())
{
// stores IndexOut
IdxOut = IndexOut;
if (IdxOut < Max) IdxOut++;
else IdxOut = 0;
PBlock = Buffer + uintptr_t(IdxOut * FBlockSize);
// moves data
memcpy(lpdata, PBlock, FBlockSize);
// Updates IndexOut
IndexOut = IdxOut;
return true;
}
else
return false;
}
//---------------------------------------------------------------------------
bool TMsgEventQueue::Empty()
{
return (IndexIn == IndexOut);
}
//---------------------------------------------------------------------------
bool TMsgEventQueue::Full()
{
int IdxOut = IndexOut; // To avoid troubles if IndexOut changes during next line
return ( (IdxOut == IndexIn + 1) || ((IndexIn == Max) && (IdxOut == 0)));
}
//---------------------------------------------------------------------------
// WORKER THREAD
//---------------------------------------------------------------------------
TMsgWorkerThread::TMsgWorkerThread(TMsgSocket *Socket, TCustomMsgServer *Server)
{
FreeOnTerminate = true;
WorkerSocket = Socket;
FServer = Server;
}
//---------------------------------------------------------------------------
void TMsgWorkerThread::Execute()
{
bool Exception = false;
bool SelfClose = false;
// Working loop
while (!Terminated && !SelfClose && !Exception && !FServer->Destroying)
{
try
{
if (!WorkerSocket->Execute()) // False -> End of Activities
SelfClose = true;
} catch (...)
{
Exception = true;
}
};
if (!FServer->Destroying)
{
// Exception detected during Worker activity
if (Exception)
{
WorkerSocket->ForceClose();
FServer->DoEvent(WorkerSocket->ClientHandle, evcClientException, 0, 0, 0, 0, 0);
}
else
if (SelfClose)
{
FServer->DoEvent(WorkerSocket->ClientHandle, evcClientDisconnected, 0, 0, 0, 0, 0);
}
else
FServer->DoEvent(WorkerSocket->ClientHandle, evcClientTerminated, 0, 0, 0, 0, 0);
}
delete WorkerSocket;
// Delete reference from list
FServer->Delete(Index);
}
//---------------------------------------------------------------------------
// LISTENER THREAD
//---------------------------------------------------------------------------
TMsgListenerThread::TMsgListenerThread(TMsgSocket *Listener, TCustomMsgServer *Server)
{
FServer = Server;
FListener = Listener;
FreeOnTerminate = false;
}
//---------------------------------------------------------------------------
void TMsgListenerThread::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);
};
}
}
//---------------------------------------------------------------------------
// TCP SERVER
//---------------------------------------------------------------------------
TCustomMsgServer::TCustomMsgServer()
{
strcpy(FLocalAddress, "0.0.0.0");
CSList = new TSnapCriticalSection();
CSEvent = new TSnapCriticalSection();
FEventQueue = new TMsgEventQueue(MaxEvents, sizeof (TSrvEvent));
memset(Workers, 0, sizeof (Workers));
for (int i = 0; i < MaxWorkers; i++)
Workers[i] = NULL;
Status = SrvStopped;
EventMask = 0xFFFFFFFF;
LogMask = 0xFFFFFFFF;
Destroying = false;
FLastError = 0;
ClientsCount = 0;
LocalBind = 0;
MaxClients = MaxWorkers;
OnEvent = NULL;
}
//---------------------------------------------------------------------------
TCustomMsgServer::~TCustomMsgServer()
{
Destroying = true;
Stop();
OnEvent = NULL;
delete CSList;
delete CSEvent;
delete FEventQueue;
}
//---------------------------------------------------------------------------
void TCustomMsgServer::LockList()
{
CSList->Enter();
}
//---------------------------------------------------------------------------
void TCustomMsgServer::UnlockList()
{
CSList->Leave();
}
//---------------------------------------------------------------------------
int TCustomMsgServer::FirstFree()
{
int i;
for (i = 0; i < MaxWorkers; i++)
{
if (Workers[i] == 0)
return i;
}
return -1;
}
//---------------------------------------------------------------------------
int TCustomMsgServer::StartListener()
{
int Result;
// Creates the listener
SockListener = new TMsgSocket();
strncpy(SockListener->LocalAddress, FLocalAddress, 16);
SockListener->LocalPort = LocalPort;
// Binds
Result = SockListener->SckBind();
if (Result == 0)
{
LocalBind = SockListener->LocalBind;
// Listen
Result = SockListener->SckListen();
if (Result == 0)
{
// Creates the Listener thread
ServerThread = new TMsgListenerThread(SockListener, this);
ServerThread->Start();
}
else
delete SockListener;
}
else
delete SockListener;
return Result;
}
//---------------------------------------------------------------------------
void TCustomMsgServer::TerminateAll()
{
int c;
longword Elapsed;
bool Timeout;
if (ClientsCount > 0)
{
for (c = 0; c < MaxWorkers; c++)
{
if (Workers[c] != 0)
PMsgWorkerThread(Workers[c])->Terminate();
}
// Wait for closing
Elapsed = SysGetTick();
Timeout = false;
while (!Timeout && (ClientsCount > 0))
{
Timeout = DeltaTime(Elapsed) > WkTimeout;
if (!Timeout)
SysSleep(100);
};
if (ClientsCount > 0)
KillAll(); // one o more threads are hanged
ClientsCount = 0;
}
}
//---------------------------------------------------------------------------
void TCustomMsgServer::KillAll()
{
int c, cnt = 0;
LockList();
for (c = 0; c < MaxWorkers; c++)
{
if (Workers[c] != 0)
try
{
PMsgWorkerThread(Workers[c])->Kill();
PMsgWorkerThread(Workers[c])->WorkerSocket->ForceClose();
delete PMsgWorkerThread(Workers[c]);
Workers[c] = 0;
cnt++;
} catch (...)
{
};
}
UnlockList();
DoEvent(0, evcClientsDropped, 0, cnt, 0, 0, 0);
}
//---------------------------------------------------------------------------
bool TCustomMsgServer::CanAccept(socket_t Socket)
{
return ((MaxClients == 0) || (ClientsCount < MaxClients));
}
//---------------------------------------------------------------------------
PWorkerSocket TCustomMsgServer::CreateWorkerSocket(socket_t Sock)
{
PWorkerSocket Result;
// Creates a funny default class : a tcp echo worker
Result = new TEcoTcpWorker();
Result->SetSocket(Sock);
return Result;
}
//---------------------------------------------------------------------------
void TCustomMsgServer::DoEvent(int Sender, longword Code, word RetCode, word Param1, word Param2, word Param3, word Param4)
{
TSrvEvent SrvEvent;
bool GoLog = (Code & LogMask) != 0;
bool GoEvent = (Code & EventMask) != 0;
if (!Destroying && (GoLog || GoEvent))
{
CSEvent->Enter();
time(&SrvEvent.EvtTime);
SrvEvent.EvtSender = Sender;
SrvEvent.EvtCode = Code;
SrvEvent.EvtRetCode = RetCode;
SrvEvent.EvtParam1 = Param1;
SrvEvent.EvtParam2 = Param2;
SrvEvent.EvtParam3 = Param3;
SrvEvent.EvtParam4 = Param4;
if (GoEvent && (OnEvent != NULL))
try
{ // callback is outside here, we have to shield it
OnEvent(FUsrPtr, &SrvEvent, sizeof (TSrvEvent));
} catch (...)
{
};
if (GoLog)
FEventQueue->Insert(&SrvEvent);
CSEvent->Leave();
};
}
//---------------------------------------------------------------------------
void TCustomMsgServer::Delete(int Index)
{
LockList();
Workers[Index] = 0;
ClientsCount--;
UnlockList();
}
//---------------------------------------------------------------------------
void TCustomMsgServer::Incoming(socket_t Sock)
{
int idx;
PWorkerSocket WorkerSocket;
longword ClientHandle = Msg_GetSockAddr(Sock);
if (CanAccept(Sock))
{
LockList();
// First position available in the thread buffer
idx = FirstFree();
if (idx >= 0)
{
// Creates the Worker and assigns it the connected socket
WorkerSocket = CreateWorkerSocket(Sock);
// Creates the Worker thread
Workers[idx] = new TMsgWorkerThread(WorkerSocket, this);
PMsgWorkerThread(Workers[idx])->Index = idx;
// Update the number
ClientsCount++;
// And Starts the worker
PMsgWorkerThread(Workers[idx])->Start();
DoEvent(WorkerSocket->ClientHandle, evcClientAdded, 0, 0, 0, 0, 0);
}
else
{
DoEvent(ClientHandle, evcClientNoRoom, 0, 0, 0, 0, 0);
Msg_CloseSocket(Sock);
}
UnlockList();
}
else
{
Msg_CloseSocket(Sock);
DoEvent(ClientHandle, evcClientRejected, 0, 0, 0, 0, 0);
};
}
//---------------------------------------------------------------------------
int TCustomMsgServer::Start()
{
int Result = 0;
if (Status != SrvRunning)
{
Result = StartListener();
if (Result != 0)
{
DoEvent(0, evcListenerCannotStart, Result, 0, 0, 0, 0);
Status = SrvError;
}
else
{
DoEvent(0, evcServerStarted, SockListener->ClientHandle, LocalPort, 0, 0, 0);
Status = SrvRunning;
};
};
FLastError = Result;
return Result;
}
//---------------------------------------------------------------------------
int TCustomMsgServer::StartTo(const char *Address, word Port)
{
strncpy(FLocalAddress, Address, 16);
LocalPort = Port;
return Start();
}
//---------------------------------------------------------------------------
void TCustomMsgServer::Stop()
{
if (Status == SrvRunning)
{
// Kills the listener thread
ServerThread->Terminate();
if (ServerThread->WaitFor(ThTimeout) != WAIT_OBJECT_0)
ServerThread->Kill();
delete ServerThread;
// Kills the listener
delete SockListener;
// Terminate all client threads
TerminateAll();
Status = SrvStopped;
LocalBind = 0;
DoEvent(0, evcServerStopped, 0, 0, 0, 0, 0);
};
FLastError = 0;
}
//---------------------------------------------------------------------------
int TCustomMsgServer::SetEventsCallBack(pfn_SrvCallBack PCallBack, void *UsrPtr)
{
OnEvent = PCallBack;
FUsrPtr = UsrPtr;
return 0;
}
//---------------------------------------------------------------------------
bool TCustomMsgServer::PickEvent(void *pEvent)
{
try
{
return FEventQueue->Extract(pEvent);
} catch (...)
{
return false;
};
}
//---------------------------------------------------------------------------
bool TCustomMsgServer::EventEmpty()
{
return FEventQueue->Empty();
}
//---------------------------------------------------------------------------
void TCustomMsgServer::EventsFlush()
{
CSEvent->Enter();
FEventQueue->Flush();
CSEvent->Leave();
}
//---------------------------------------------------------------------------

View File

@@ -0,0 +1,247 @@
/*=============================================================================|
| 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 snap_tcpsrvr_h
#define snap_tcpsrvr_h
//---------------------------------------------------------------------------
#include "snap_msgsock.h"
#include "snap_threads.h"
//---------------------------------------------------------------------------
#define MaxWorkers 1024
#define MaxEvents 1500
const int SrvStopped = 0;
const int SrvRunning = 1;
const int SrvError = 2;
const longword evcServerStarted = 0x00000001;
const longword evcServerStopped = 0x00000002;
const longword evcListenerCannotStart = 0x00000004;
const longword evcClientAdded = 0x00000008;
const longword evcClientRejected = 0x00000010;
const longword evcClientNoRoom = 0x00000020;
const longword evcClientException = 0x00000040;
const longword evcClientDisconnected = 0x00000080;
const longword evcClientTerminated = 0x00000100;
const longword evcClientsDropped = 0x00000200;
const longword evcReserved_00000400 = 0x00000400;
const longword evcReserved_00000800 = 0x00000800;
const longword evcReserved_00001000 = 0x00001000;
const longword evcReserved_00002000 = 0x00002000;
const longword evcReserved_00004000 = 0x00004000;
const longword evcReserved_00008000 = 0x00008000;
// Server Interface errors
const longword errSrvBase = 0x0000FFFF;
const longword errSrvMask = 0xFFFF0000;
const longword errSrvCannotStart = 0x00100000;
const longword ThTimeout = 2000; // Thread timeout
const longword WkTimeout = 3000; // Workers termination timeout
#pragma pack(1)
typedef struct{
time_t EvtTime; // Timestamp
int EvtSender; // Sender
longword EvtCode; // Event code
word EvtRetCode; // Event result
word EvtParam1; // Param 1 (if available)
word EvtParam2; // Param 2 (if available)
word EvtParam3; // Param 3 (if available)
word EvtParam4; // Param 4 (if available)
}TSrvEvent, *PSrvEvent;
extern "C"
{
typedef void (S7API *pfn_SrvCallBack)(void * usrPtr, PSrvEvent PEvent, int Size);
}
#pragma pack()
//---------------------------------------------------------------------------
// EVENTS QUEUE
//---------------------------------------------------------------------------
class TMsgEventQueue
{
private:
int IndexIn; // <-- insert index
int IndexOut; // --> extract index
int Max; // Buffer upper bound [0..Max]
int FCapacity; // Queue capacity
pbyte Buffer;
int FBlockSize;
public:
TMsgEventQueue(const int Capacity, const int BlockSize);
~TMsgEventQueue();
void Flush();
void Insert(void *lpdata);
bool Extract(void *lpdata);
bool Empty();
bool Full();
};
typedef TMsgEventQueue *PMsgEventQueue;
//---------------------------------------------------------------------------
// WORKER THREAD
//---------------------------------------------------------------------------
class TCustomMsgServer; // forward declaration
// It's created when connection is accepted, it will interface with the client.
class TMsgWorkerThread : public TSnapThread
{
private:
TCustomMsgServer *FServer;
protected:
TMsgSocket *WorkerSocket;
public:
int Index;
friend class TCustomMsgServer;
TMsgWorkerThread(TMsgSocket *Socket, TCustomMsgServer *Server);
void Execute();
};
typedef TMsgWorkerThread *PMsgWorkerThread;
//---------------------------------------------------------------------------
// LISTENER THREAD
//---------------------------------------------------------------------------
// It listens for incoming connection.
class TMsgListenerThread : public TSnapThread
{
private:
TMsgSocket *FListener;
TCustomMsgServer *FServer;
public:
TMsgListenerThread(TMsgSocket *Listener, TCustomMsgServer *Server);
void Execute();
};
typedef TMsgListenerThread *PMsgListenerThread;
//---------------------------------------------------------------------------
// TCP SERVER
//---------------------------------------------------------------------------
typedef TMsgSocket *PWorkerSocket;
class TCustomMsgServer
{
private:
int FLastError;
char FLocalAddress[16];
// Socket listener
PMsgSocket SockListener;
// Server listener
PMsgListenerThread ServerThread;
// Critical section to lock Workers list activities
PSnapCriticalSection CSList;
// Event queue
PMsgEventQueue FEventQueue;
// Callback related
pfn_SrvCallBack OnEvent;
void *FUsrPtr;
// private methods
int StartListener();
void LockList();
void UnlockList();
int FirstFree();
protected:
bool Destroying;
// Critical section to lock Event activities
PSnapCriticalSection CSEvent;
// Workers list
void *Workers[MaxWorkers];
// Terminates all worker threads
virtual void TerminateAll();
// Kills all worker threads that are unresponsive
void KillAll();
// if (true the connection is accepted, otherwise the connection
// is closed gracefully
virtual bool CanAccept(socket_t Socket);
// Returns the class of the worker socket, override it for real servers
virtual PWorkerSocket CreateWorkerSocket(socket_t Sock);
// Handles the event
virtual void DoEvent(int Sender, longword Code, word RetCode, word Param1,
word Param2, word Param3, word Param4);
// Delete the worker from the list (It's invoked by Worker Thread)
void Delete(int Index);
// Incoming connection (It's invoked by ServerThread, the listener)
virtual void Incoming(socket_t Sock);
public:
friend class TMsgWorkerThread;
friend class TMsgListenerThread;
word LocalPort;
longword LocalBind;
longword LogMask;
longword EventMask;
int Status;
int ClientsCount;
int MaxClients;
TCustomMsgServer();
virtual ~TCustomMsgServer();
// Starts the server
int Start();
int StartTo(const char *Address, word Port);
// Stops the server
void Stop();
// Sets Event callback
int SetEventsCallBack(pfn_SrvCallBack PCallBack, void *UsrPtr);
// Pick an event from the circular queue
bool PickEvent(void *pEvent);
// Returns true if (the Event queue is empty
bool EventEmpty();
// Flushes Event queue
void EventsFlush();
};
//---------------------------------------------------------------------------
// TCP WORKER
//---------------------------------------------------------------------------
// Default worker class, a simply tcp echo to test the connection and
// data I/O to use the server outside the project
class TEcoTcpWorker : public TMsgSocket
{
public:
bool Execute()
{
byte Buffer[4096];
int Size;
if (CanRead(WorkInterval)) // Small time to avoid time wait during the close
{
Receive(&Buffer,sizeof(Buffer),Size);
if ((LastTcpError==0) && (Size>0))
{
SendPacket(&Buffer,Size);
return LastTcpError==0;
}
else
return false;
}
else
return true;
};
};
//---------------------------------------------------------------------------
#endif // snap_tcpsrvr_h

View File

@@ -0,0 +1,162 @@
/*=============================================================================|
| 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 "snap_threads.h"
//---------------------------------------------------------------------------
#ifdef OS_WINDOWS
DWORD WINAPI ThreadProc(LPVOID param)
#else
void* ThreadProc(void* param)
#endif
{
PSnapThread Thread;
// Unix but not Solaris
#if (defined(POSIX) || defined(OS_OSX)) && (!defined(OS_SOLARIS_NATIVE_THREADS))
int last_type, last_state;
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &last_type);
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &last_state);
#endif
Thread = PSnapThread(param);
if (!Thread->Terminated)
try
{
Thread->Execute();
} catch (...)
{
};
Thread->Closed = true;
if (Thread->FreeOnTerminate)
{
delete Thread;
};
#ifdef OS_WINDOWS
ExitThread(0);
#endif
#if defined(POSIX) && (!defined(OS_SOLARIS_NATIVE_THREADS))
pthread_exit((void*)0);
#endif
#if defined(OS_OSX)
pthread_exit((void*)0);
#endif
#ifdef OS_SOLARIS_NATIVE_THREADS
thr_exit((void*)0);
#endif
return 0; // never reach, only to avoid compiler warning
}
//---------------------------------------------------------------------------
TSnapThread::TSnapThread()
{
Started = false;
Closed=false;
Terminated = false;
FreeOnTerminate = false;
}
//---------------------------------------------------------------------------
TSnapThread::~TSnapThread()
{
if (Started && !Closed)
{
Terminate();
Join();
};
#ifdef OS_WINDOWS
if (Started)
CloseHandle(th);
#endif
}
//---------------------------------------------------------------------------
void TSnapThread::ThreadCreate()
{
#ifdef OS_WINDOWS
th = CreateThread(0, 0, ThreadProc, this, 0, 0);
#endif
#if defined(POSIX) && (!defined(OS_SOLARIS_NATIVE_THREADS))
pthread_attr_t a;
pthread_attr_init(&a);
pthread_attr_setdetachstate(&a, PTHREAD_CREATE_DETACHED);
pthread_create(&th, &a, &ThreadProc, this);
#endif
#if defined(OS_OSX)
pthread_create(&th, 0, &ThreadProc, this);
#endif
#ifdef OS_SOLARIS_NATIVE_THREADS
thr_create(0, // default stack base
0, // default stack size
&ThreadProc, // Thread routine
this, // argument
0,
&th);
#endif
}
//---------------------------------------------------------------------------
void TSnapThread::Start()
{
if (!Started)
{
ThreadCreate();
Started = true;
}
}
//---------------------------------------------------------------------------
void TSnapThread::Terminate()
{
Terminated = true;
}
//---------------------------------------------------------------------------
void TSnapThread::Kill()
{
if (Started && !Closed)
{
ThreadKill();
Closed = true;
}
}
//---------------------------------------------------------------------------
void TSnapThread::Join()
{
if (Started && !Closed)
{
ThreadJoin();
Closed = true;
}
}
//---------------------------------------------------------------------------
longword TSnapThread::WaitFor(uint64_t Timeout)
{
if (Started)
{
if (!Closed)
return ThreadWait(Timeout);
else
return WAIT_OBJECT_0;
}
else
return WAIT_OBJECT_0;
}
//---------------------------------------------------------------------------

View File

@@ -0,0 +1,45 @@
/*=============================================================================|
| 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 snap_threads_h
#define snap_threads_h
//---------------------------------------------------------------------------
#include "snap_platform.h"
#ifdef OS_WINDOWS
# include "win_threads.h"
#endif
#if defined(POSIX) && (!defined(OS_SOLARIS_NATIVE_THREADS))
# include "unix_threads.h"
#endif
#ifdef OS_SOLARIS_NATIVE_THREADS
# include "sol_threads.h"
#endif
#if defined(OS_OSX)
# include "unix_threads.h"
#endif
//---------------------------------------------------------------------------
#endif // snap_threads_h

View File

@@ -0,0 +1,208 @@
/*=============================================================================|
| 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/ |
|==============================================================================|
| |
| Solaris 11 Threads support |
| |
|=============================================================================*/
#ifndef sol_threads_h
#define sol_threads_h
//---------------------------------------------------------------------------
#include "snap_platform.h"
#include "snap_sysutils.h"
#include <thread.h>
#include <synch.h>
//---------------------------------------------------------------------------
class TSnapCriticalSection {
private:
mutex_t mx;
int result;
public:
TSnapCriticalSection() {
mutex_init(&mx, USYNC_THREAD, 0);
};
~TSnapCriticalSection() {
mutex_destroy(&mx);
};
void Enter() {
mutex_lock(&mx);
};
void Leave() {
mutex_unlock(&mx);
};
bool TryEnter() {
return mutex_trylock(&mx) == 0;
};
};
typedef TSnapCriticalSection *PSnapCriticalSection;
//---------------------------------------------------------------------------
const longword WAIT_OBJECT_0 = 0x00000000L;
const longword WAIT_ABANDONED = 0x00000080L;
const longword WAIT_TIMEOUT = 0x00000102L;
const longword WAIT_FAILED = 0xFFFFFFFFL;
class TSnapEvent {
private:
cond_t CVariable;
mutex_t Mutex;
bool AutoReset;
bool State;
public:
TSnapEvent(bool ManualReset)
{
AutoReset = !ManualReset;
cond_init(&CVariable, USYNC_THREAD, 0) == 0;
mutex_init(&Mutex, USYNC_THREAD, 0);
State = false;
}
~TSnapEvent()
{
cond_destroy(&CVariable);
mutex_destroy(&Mutex);
};
void Set()
{
mutex_lock(&Mutex);
State = true;
if (AutoReset)
cond_signal(&CVariable);
else
cond_broadcast(&CVariable);
mutex_unlock(&Mutex);
};
void Reset()
{
mutex_lock(&Mutex);
State = false;
mutex_unlock(&Mutex);
}
longword WaitForever()
{
mutex_lock(&Mutex);
while (!State) // <-- to avoid spurious wakeups
cond_wait(&CVariable, &Mutex);
if (AutoReset)
State = false;
mutex_unlock(&Mutex);
return WAIT_OBJECT_0;
};
longword WaitFor(int64_t Timeout)
{
longword Result = WAIT_OBJECT_0;
if (Timeout == 0)
Timeout = 1; // 0 is not allowed
if (Timeout > 0) {
mutex_lock(&Mutex);
if (!State) {
timespec ts;
timeval tv;
gettimeofday(&tv, NULL);
uint64_t nsecs = ((uint64_t) tv.tv_sec) * 1000000000 +
Timeout * 1000000 +
((uint64_t) tv.tv_usec) * 1000;
ts.tv_sec = nsecs / 1000000000;
ts.tv_nsec = (nsecs - ((uint64_t) ts.tv_sec) * 1000000000);
do
{
Result = cond_timedwait(&CVariable, &Mutex, &ts);
if (Result == ETIMEDOUT)
Result = WAIT_TIMEOUT;
}
while (Result == 0 && !State);
}
else
if (AutoReset) // take the ownership
State = false;
mutex_unlock(&Mutex);
return Result;
}
else // Timeout<0
return WaitForever();
};
};
typedef TSnapEvent *PSnapEvent;
//---------------------------------------------------------------------------
class TSnapThread {
private:
thread_t th;
bool FCreateSuspended;
void ThreadCreate();
void ThreadJoin()
{
thr_join(th, 0, 0);
};
void ThreadKill()
{
thr_kill(th, 0);
};
longword ThreadWait(uint64_t Timeout)
{
longword Elapsed = SysGetTick();
while (!Closed && !(DeltaTime(Elapsed) > Timeout))
SysSleep(100);
if (Closed)
return WAIT_OBJECT_0;
else
return WAIT_TIMEOUT;
};
protected:
bool Started;
public:
bool Terminated;
bool Closed;
bool FreeOnTerminate;
TSnapThread();
virtual ~TSnapThread();
virtual void Execute() {
};
void Start();
void Terminate();
void Kill();
void Join();
longword WaitFor(uint64_t Timeout);
};
typedef TSnapThread *PSnapThread;
//---------------------------------------------------------------------------
#endif // sol_threads_h

View File

@@ -0,0 +1,228 @@
/*=============================================================================|
| 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/ |
|==============================================================================|
| |
| Posix Threads support (Linux, FreeBSD) |
| |
|=============================================================================*/
#ifndef unix_threads_h
#define unix_threads_h
//---------------------------------------------------------------------------
#include "snap_platform.h"
#include "snap_sysutils.h"
#include <semaphore.h>
#include <pthread.h>
//---------------------------------------------------------------------------
class TSnapCriticalSection
{
private:
pthread_mutex_t mx;
// int result;
public:
TSnapCriticalSection()
{
/*
This would be the best code, but very often it causes a segmentation fault in many
unix systems (the problem seems to be pthread_mutexattr_destroy()).
So, to avoid problems in future kernel/libc release, we use the "safe" default.
pthread_mutexattr_t mxAttr;
pthread_mutexattr_settype(&mxAttr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&mx, &mxAttr);
pthread_mutexattr_destroy(&mxAttr);
*/
pthread_mutex_init(&mx, 0);
};
~TSnapCriticalSection()
{
pthread_mutex_destroy(&mx);
};
void Enter()
{
pthread_mutex_lock(&mx);
};
void Leave()
{
pthread_mutex_unlock(&mx);
};
bool TryEnter()
{
return pthread_mutex_trylock(&mx) == 0;
};
};
typedef TSnapCriticalSection *PSnapCriticalSection;
//---------------------------------------------------------------------------
const longword WAIT_OBJECT_0 = 0x00000000L;
const longword WAIT_ABANDONED = 0x00000080L;
const longword WAIT_TIMEOUT = 0x00000102L;
const longword WAIT_FAILED = 0xFFFFFFFFL;
class TSnapEvent
{
private:
pthread_cond_t CVariable;
pthread_mutex_t Mutex;
bool AutoReset;
bool State;
public:
TSnapEvent(bool ManualReset)
{
AutoReset = !ManualReset;
if (pthread_cond_init(&CVariable, 0) == 0)
pthread_mutex_init(&Mutex, 0);
State = false;
}
~TSnapEvent()
{
pthread_cond_destroy(&CVariable);
pthread_mutex_destroy(&Mutex);
};
void Set()
{
pthread_mutex_lock(&Mutex);
State = true;
if (AutoReset)
pthread_cond_signal(&CVariable);
else
pthread_cond_broadcast(&CVariable);
pthread_mutex_unlock(&Mutex);
};
void Reset()
{
pthread_mutex_lock(&Mutex);
State = false;
pthread_mutex_unlock(&Mutex);
}
longword WaitForever()
{
pthread_mutex_lock(&Mutex);
while (!State) // <-- to avoid spurious wakeups
pthread_cond_wait(&CVariable, &Mutex);
if (AutoReset)
State = false;
pthread_mutex_unlock(&Mutex);
return WAIT_OBJECT_0;
};
longword WaitFor(int64_t Timeout)
{
longword Result = WAIT_OBJECT_0;
if (Timeout == 0)
Timeout = 1; // 0 is not allowed
if (Timeout > 0)
{
pthread_mutex_lock(&Mutex);
if (!State)
{
timespec ts;
timeval tv;
gettimeofday(&tv, NULL);
uint64_t nsecs = ((uint64_t) tv.tv_sec) * 1000000000 +
Timeout * 1000000 +
((uint64_t) tv.tv_usec) * 1000;
ts.tv_sec = nsecs / 1000000000;
ts.tv_nsec = (nsecs - ((uint64_t) ts.tv_sec) * 1000000000);
do {
Result = pthread_cond_timedwait(&CVariable, &Mutex, &ts);
if (Result == ETIMEDOUT)
Result = WAIT_TIMEOUT;
} while (Result == 0 && !State);
}
else
if (AutoReset) // take the ownership
State = false;
pthread_mutex_unlock(&Mutex);
return Result;
}
else // Timeout<0
return WaitForever();
};
};
typedef TSnapEvent *PSnapEvent;
//---------------------------------------------------------------------------
class TSnapThread
{
private:
pthread_t th;
bool FCreateSuspended;
void ThreadCreate();
void ThreadJoin()
{
pthread_join(th, 0);
};
void ThreadKill()
{
pthread_cancel(th);
};
longword ThreadWait(uint64_t Timeout)
{
longword Elapsed = SysGetTick();
while (!Closed && !(DeltaTime(Elapsed) > Timeout))
SysSleep(100);
if (Closed)
return WAIT_OBJECT_0;
else
return WAIT_TIMEOUT;
};
protected:
bool Started;
public:
bool Terminated;
bool Closed;
bool FreeOnTerminate;
TSnapThread();
virtual ~TSnapThread();
virtual void Execute()
{
};
void Start();
void Terminate();
void Kill();
void Join();
longword WaitFor(uint64_t Timeout);
};
typedef TSnapThread *PSnapThread;
//---------------------------------------------------------------------------
#endif // unix_threads_h

View File

@@ -0,0 +1,159 @@
/*=============================================================================|
| 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/ |
|==============================================================================|
| |
| Windows Threads support (Windows, ReactOS) |
| |
|=============================================================================*/
#ifndef win_threads_h
#define win_threads_h
//---------------------------------------------------------------------------
#include "snap_platform.h"
#include "snap_sysutils.h"
//---------------------------------------------------------------------------
class TSnapCriticalSection
{
private:
CRITICAL_SECTION cs;
public:
TSnapCriticalSection()
{
InitializeCriticalSection(&cs);
};
~TSnapCriticalSection()
{
DeleteCriticalSection(&cs);
};
void Enter()
{
EnterCriticalSection(&cs);
};
void Leave()
{
LeaveCriticalSection(&cs);
};
bool TryEnter()
{
return (TryEnterCriticalSection(&cs) != 0);
};
};
typedef TSnapCriticalSection *PSnapCriticalSection;
//---------------------------------------------------------------------------
class TSnapEvent
{
private:
HANDLE Event;
public:
TSnapEvent(bool ManualReset)
{
Event = CreateEvent(0, ManualReset, false, 0);
};
~TSnapEvent()
{
if (Event != 0)
CloseHandle(Event);
};
void Set()
{
if (Event != 0)
SetEvent(Event);
};
void Reset()
{
if (Event != 0)
ResetEvent(Event);
};
longword WaitForever()
{
if (Event != 0)
return WaitForSingleObject(Event, INFINITE);
else
return WAIT_FAILED;
};
longword WaitFor(int64_t Timeout) {
if (Event != 0)
return WaitForSingleObject(Event, DWORD(Timeout));
else
return WAIT_FAILED;
};
};
typedef TSnapEvent *PSnapEvent;
//---------------------------------------------------------------------------
class TSnapThread {
private:
HANDLE th;
bool FCreateSuspended;
void ThreadCreate();
void ThreadJoin()
{
WaitForSingleObject(th, INFINITE);
};
void ThreadKill()
{
TerminateThread(th, 0);
};
longword ThreadWait(uint64_t Timeout)
{
return WaitForSingleObject(th, DWORD(Timeout));
};
protected:
bool Started;
public:
bool Terminated;
bool Closed;
bool FreeOnTerminate;
TSnapThread();
virtual ~TSnapThread();
virtual void Execute()
{
};
void Start();
void Terminate();
void Kill();
void Join();
longword WaitFor(uint64_t Timeout);
};
typedef TSnapThread *PSnapThread;
//---------------------------------------------------------------------------
#endif // win_threads_h

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,950 @@
/*=============================================================================|
| PROJECT SNAP7 1.4.3 |
|==============================================================================|
| Copyright (C) 2013, 2025 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/ |
|==============================================================================|
| |
| This file is a modified wrapper which contains OpenPLC interface |
| |
|=============================================================================*/
#ifndef snap7_h
#define snap7_h
//---------------------------------------------------------------------------
// Platform detection
//---------------------------------------------------------------------------
#if defined (_WIN32)||defined(_WIN64)||defined(__WIN32__)||defined(__WINDOWS__)
# define OS_WINDOWS
#endif
#if defined(unix) || defined(__unix__) || defined(__unix)
# define PLATFORM_UNIX
#endif
#if defined(__SVR4) || defined(__svr4__)
# define OS_SOLARIS
#endif
#if BSD>=0
# define OS_BSD
#endif
#if defined(__APPLE__)
# define OS_OSX
#endif
#if defined(PLATFORM_UNIX) || defined(OS_OSX)
# include <unistd.h>
# if defined(_POSIX_VERSION)
# define POSIX
# endif
#endif
//---------------------------------------------------------------------------
// C++ Library
//---------------------------------------------------------------------------
#ifdef __cplusplus
// CANNOT INCLUDE time.h here. We will use a workaround for tm struct and time_t
// #include <time.h>
# include <string.h>
// Visual C++ not C99 compliant (VS2008--)
#ifdef _MSC_VER
# if _MSC_VER >= 1600
# include <stdint.h> // VS2010++ have it
# else
typedef signed __int8 int8_t;
typedef signed __int16 int16_t;
typedef signed __int32 int32_t;
typedef signed __int64 int64_t;
typedef unsigned __int8 uint8_t;
typedef unsigned __int16 uint16_t;
typedef unsigned __int32 uint32_t;
typedef unsigned __int64 uint64_t;
#ifdef _WIN64
typedef unsigned __int64 uintptr_t;
#else
typedef unsigned __int32 uintptr_t;
#endif
# endif
#else
# include <stdint.h>
#endif
extern "C" {
#endif
//---------------------------------------------------------------------------
// C exact length types
//---------------------------------------------------------------------------
#ifndef __cplusplus
#ifdef OS_BSD
# include <stdint.h>
#endif
#ifdef OS_OSX
# include <stdint.h>
#endif
#ifdef OS_SOLARIS
# include <stdint.h>
#endif
#if defined(_UINTPTR_T_DEFINED)
# include <stdint.h>
#endif
#if !defined(_UINTPTR_T_DEFINED) && !defined(OS_SOLARIS) && !defined(OS_BSD) && !defined(OS_OSX)
typedef unsigned char uint8_t; // 8 bit unsigned integer
typedef unsigned short uint16_t; // 16 bit unsigned integer
typedef unsigned int uint32_t; // 32 bit unsigned integer
typedef unsigned long uintptr_t;// 64 bit unsigned integer
#endif
#endif
#ifdef OS_WINDOWS
# define S7API __stdcall
#else
# define S7API
#endif
#define _s7ok 0
#define _s7error 1
#pragma pack(1)
//******************************************************************************
// COMMON
//******************************************************************************
// Exact length types regardless of platform/processor
typedef uint8_t byte;
typedef uint16_t word;
typedef uint32_t longword;
typedef byte *pbyte;
typedef word *pword;
typedef uintptr_t S7Object; // multi platform/processor object reference
// DON'T CONFUSE IT WITH AN OLE OBJECT, IT'S SIMPLY
// AN INTEGER VALUE (32 OR 64 BIT) USED AS HANDLE.
#ifndef __cplusplus
typedef int bool;
#define false 0;
#define true 1;
#endif
typedef struct
{
int tm_sec;
int tm_min;
int tm_hour;
int tm_mday;
int tm_mon;
int tm_year;
int tm_wday;
int tm_yday;
int tm_isdst;
}s7tm;
const int errLibInvalidParam = -1;
const int errLibInvalidObject = -2;
// CPU status
#define S7CpuStatusUnknown 0x00
#define S7CpuStatusRun 0x08
#define S7CpuStatusStop 0x04
// ISO Errors
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
// Tag Struct
typedef struct{
int Area;
int DBNumber;
int Start;
int Elements;
int WordLen;
}TS7Tag, *PS7Tag;
//------------------------------------------------------------------------------
// PARAMS LIST
//------------------------------------------------------------------------------
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;
// Client/Partner Job status
const int JobComplete = 0;
const int JobPending = 1;
//******************************************************************************
// CLIENT
//******************************************************************************
// Error codes
const longword errNegotiatingPDU = 0x00100000;
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 int MaxVars = 20; // Max vars that can be transferred with MultiRead/MultiWrite
// Client Connection Type
const word CONNTYPE_PG = 0x0001; // Connect to the PLC as a PG
const word CONNTYPE_OP = 0x0002; // Connect to the PLC as an OP
const word CONNTYPE_BASIC = 0x0003; // Basic connection
// 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;
// Word Length
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;
// 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;
// List Blocks
typedef struct {
int OBCount;
int FBCount;
int FCCount;
int SFBCount;
int SFCCount;
int DBCount;
int SDBCount;
} TS7BlocksList, *PS7BlocksList;
// Blocks info
typedef struct {
int BlkType; // Block Type (OB, DB)
int BlkNumber; // Block number
int BlkLang; // Block Language
int BlkFlags; // Block flags
int MC7Size; // The real size in bytes
int LoadSize; // Load memory size
int LocalData; // Local data
int SBBLength; // SBB Length
int CheckSum; // Checksum
int Version; // Block version
// Chars info
char CodeDate[11]; // Code date
char IntfDate[11]; // Interface date
char Author[9]; // Author
char Family[9]; // Family
char Header[9]; // Header
} TS7BlockInfo, *PS7BlockInfo ;
typedef word TS7BlocksOfType[0x2000];
typedef TS7BlocksOfType *PS7BlocksOfType;
// Order code
typedef struct {
char Code[21];
byte V1;
byte V2;
byte V3;
} TS7OrderCode, *PS7OrderCode;
// CPU Info
typedef struct {
char ModuleTypeName[33];
char SerialNumber[25];
char ASName[25];
char Copyright[27];
char ModuleName[25];
} TS7CpuInfo, *PS7CpuInfo;
// CP Info
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;
// Client completion callback
typedef void (S7API *pfn_CliCompletion) (void *usrPtr, int opCode, int opResult);
//------------------------------------------------------------------------------
// Import prototypes
//------------------------------------------------------------------------------
S7Object S7API Cli_Create();
void S7API Cli_Destroy(S7Object *Client);
int S7API Cli_ConnectTo(S7Object Client, const char *Address, int Rack, int Slot);
int S7API Cli_SetConnectionParams(S7Object Client, const char *Address, word LocalTSAP, word RemoteTSAP);
int S7API Cli_SetConnectionType(S7Object Client, word ConnectionType);
int S7API Cli_Connect(S7Object Client);
int S7API Cli_Disconnect(S7Object Client);
int S7API Cli_GetParam(S7Object Client, int ParamNumber, void *pValue);
int S7API Cli_SetParam(S7Object Client, int ParamNumber, void *pValue);
int S7API Cli_SetAsCallback(S7Object Client, pfn_CliCompletion pCompletion, void *usrPtr);
// Data I/O main functions
int S7API Cli_ReadArea(S7Object Client, int Area, int DBNumber, int Start, int Amount, int WordLen, void *pUsrData);
int S7API Cli_WriteArea(S7Object Client, int Area, int DBNumber, int Start, int Amount, int WordLen, void *pUsrData);
int S7API Cli_ReadMultiVars(S7Object Client, PS7DataItem Item, int ItemsCount);
int S7API Cli_WriteMultiVars(S7Object Client, PS7DataItem Item, int ItemsCount);
// Data I/O Lean functions
int S7API Cli_DBRead(S7Object Client, int DBNumber, int Start, int Size, void *pUsrData);
int S7API Cli_DBWrite(S7Object Client, int DBNumber, int Start, int Size, void *pUsrData);
int S7API Cli_MBRead(S7Object Client, int Start, int Size, void *pUsrData);
int S7API Cli_MBWrite(S7Object Client, int Start, int Size, void *pUsrData);
int S7API Cli_EBRead(S7Object Client, int Start, int Size, void *pUsrData);
int S7API Cli_EBWrite(S7Object Client, int Start, int Size, void *pUsrData);
int S7API Cli_ABRead(S7Object Client, int Start, int Size, void *pUsrData);
int S7API Cli_ABWrite(S7Object Client, int Start, int Size, void *pUsrData);
int S7API Cli_TMRead(S7Object Client, int Start, int Amount, void *pUsrData);
int S7API Cli_TMWrite(S7Object Client, int Start, int Amount, void *pUsrData);
int S7API Cli_CTRead(S7Object Client, int Start, int Amount, void *pUsrData);
int S7API Cli_CTWrite(S7Object Client, int Start, int Amount, void *pUsrData);
// Directory functions
int S7API Cli_ListBlocks(S7Object Client, TS7BlocksList *pUsrData);
int S7API Cli_GetAgBlockInfo(S7Object Client, int BlockType, int BlockNum, TS7BlockInfo *pUsrData);
int S7API Cli_GetPgBlockInfo(S7Object Client, void *pBlock, TS7BlockInfo *pUsrData, int Size);
int S7API Cli_ListBlocksOfType(S7Object Client, int BlockType, TS7BlocksOfType *pUsrData, int *ItemsCount);
// Blocks functions
int S7API Cli_Upload(S7Object Client, int BlockType, int BlockNum, void *pUsrData, int *Size);
int S7API Cli_FullUpload(S7Object Client, int BlockType, int BlockNum, void *pUsrData, int *Size);
int S7API Cli_Download(S7Object Client, int BlockNum, void *pUsrData, int Size);
int S7API Cli_Delete(S7Object Client, int BlockType, int BlockNum);
int S7API Cli_DBGet(S7Object Client, int DBNumber, void *pUsrData, int *Size);
int S7API Cli_DBFill(S7Object Client, int DBNumber, int FillChar);
// Date/Time functions
int S7API Cli_GetPlcDateTime(S7Object Client, s7tm *DateTime);
int S7API Cli_SetPlcDateTime(S7Object Client, s7tm *DateTime);
int S7API Cli_SetPlcSystemDateTime(S7Object Client);
// System Info functions
int S7API Cli_GetOrderCode(S7Object Client, TS7OrderCode *pUsrData);
int S7API Cli_GetCpuInfo(S7Object Client, TS7CpuInfo *pUsrData);
int S7API Cli_GetCpInfo(S7Object Client, TS7CpInfo *pUsrData);
int S7API Cli_ReadSZL(S7Object Client, int ID, int Index, TS7SZL *pUsrData, int *Size);
int S7API Cli_ReadSZLList(S7Object Client, TS7SZLList *pUsrData, int *ItemsCount);
// Control functions
int S7API Cli_PlcHotStart(S7Object Client);
int S7API Cli_PlcColdStart(S7Object Client);
int S7API Cli_PlcStop(S7Object Client);
int S7API Cli_CopyRamToRom(S7Object Client, int Timeout);
int S7API Cli_Compress(S7Object Client, int Timeout);
int S7API Cli_GetPlcStatus(S7Object Client, int *Status);
// Security functions
int S7API Cli_GetProtection(S7Object Client, TS7Protection *pUsrData);
int S7API Cli_SetSessionPassword(S7Object Client, char *Password);
int S7API Cli_ClearSessionPassword(S7Object Client);
// Low level
int S7API Cli_IsoExchangeBuffer(S7Object Client, void *pUsrData, int *Size);
// Misc
int S7API Cli_GetExecTime(S7Object Client, int *Time);
int S7API Cli_GetLastError(S7Object Client, int *LastError);
int S7API Cli_GetPduLength(S7Object Client, int *Requested, int *Negotiated);
int S7API Cli_ErrorText(int Error, char *Text, int TextLen);
// 1.1.0
int S7API Cli_GetConnected(S7Object Client, int *Connected);
//------------------------------------------------------------------------------
// Async functions
//------------------------------------------------------------------------------
int S7API Cli_AsReadArea(S7Object Client, int Area, int DBNumber, int Start, int Amount, int WordLen, void *pUsrData);
int S7API Cli_AsWriteArea(S7Object Client, int Area, int DBNumber, int Start, int Amount, int WordLen, void *pUsrData);
int S7API Cli_AsDBRead(S7Object Client, int DBNumber, int Start, int Size, void *pUsrData);
int S7API Cli_AsDBWrite(S7Object Client, int DBNumber, int Start, int Size, void *pUsrData);
int S7API Cli_AsMBRead(S7Object Client, int Start, int Size, void *pUsrData);
int S7API Cli_AsMBWrite(S7Object Client, int Start, int Size, void *pUsrData);
int S7API Cli_AsEBRead(S7Object Client, int Start, int Size, void *pUsrData);
int S7API Cli_AsEBWrite(S7Object Client, int Start, int Size, void *pUsrData);
int S7API Cli_AsABRead(S7Object Client, int Start, int Size, void *pUsrData);
int S7API Cli_AsABWrite(S7Object Client, int Start, int Size, void *pUsrData);
int S7API Cli_AsTMRead(S7Object Client, int Start, int Amount, void *pUsrData);
int S7API Cli_AsTMWrite(S7Object Client, int Start, int Amount, void *pUsrData);
int S7API Cli_AsCTRead(S7Object Client, int Start, int Amount, void *pUsrData);
int S7API Cli_AsCTWrite(S7Object Client, int Start, int Amount, void *pUsrData);
int S7API Cli_AsListBlocksOfType(S7Object Client, int BlockType, TS7BlocksOfType *pUsrData, int *ItemsCount);
int S7API Cli_AsReadSZL(S7Object Client, int ID, int Index, TS7SZL *pUsrData, int *Size);
int S7API Cli_AsReadSZLList(S7Object Client, TS7SZLList *pUsrData, int *ItemsCount);
int S7API Cli_AsUpload(S7Object Client, int BlockType, int BlockNum, void *pUsrData, int *Size);
int S7API Cli_AsFullUpload(S7Object Client, int BlockType, int BlockNum, void *pUsrData, int *Size);
int S7API Cli_AsDownload(S7Object Client, int BlockNum, void *pUsrData, int Size);
int S7API Cli_AsCopyRamToRom(S7Object Client, int Timeout);
int S7API Cli_AsCompress(S7Object Client, int Timeout);
int S7API Cli_AsDBGet(S7Object Client, int DBNumber, void *pUsrData, int *Size);
int S7API Cli_AsDBFill(S7Object Client, int DBNumber, int FillChar);
int S7API Cli_CheckAsCompletion(S7Object Client, int *opResult);
int S7API Cli_WaitAsCompletion(S7Object Client, int Timeout);
//******************************************************************************
// SERVER
//******************************************************************************
const int OperationRead = 0;
const int OperationWrite = 1;
const int mkEvent = 0;
const int mkLog = 1;
// 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;
// Errors
const longword errSrvCannotStart = 0x00100000; // Server cannot start
const longword errSrvDBNullPointer = 0x00200000; // Passed 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
// TCP Server Event codes
const longword evcServerStarted = 0x00000001;
const longword evcServerStopped = 0x00000002;
const longword evcListenerCannotStart = 0x00000004;
const longword evcClientAdded = 0x00000008;
const longword evcClientRejected = 0x00000010;
const longword evcClientNoRoom = 0x00000020;
const longword evcClientException = 0x00000040;
const longword evcClientDisconnected = 0x00000080;
const longword evcClientTerminated = 0x00000100;
const longword evcClientsDropped = 0x00000200;
const longword evcReserved_00000400 = 0x00000400; // actually unused
const longword evcReserved_00000800 = 0x00000800; // actually unused
const longword evcReserved_00001000 = 0x00001000; // actually unused
const longword evcReserved_00002000 = 0x00002000; // actually unused
const longword evcReserved_00004000 = 0x00004000; // actually unused
const longword evcReserved_00008000 = 0x00008000; // actually unused
// 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; // actually unused
const longword evcReserved_10000000 = 0x10000000; // actually unused
const longword evcReserved_20000000 = 0x20000000; // actually unused
const longword evcReserved_40000000 = 0x40000000; // actually unused
const longword evcReserved_80000000 = 0x80000000; // actually unused
// Masks to enable/disable all events
const longword evcAll = 0xFFFFFFFF;
const longword evcNone = 0x00000000;
// 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 Params : functions group
const word grProgrammer = 0x0041;
const word grCyclicData = 0x0042;
const word grBlocksInfo = 0x0043;
const word grSZL = 0x0044;
const word grPassword = 0x0045;
const word grBSend = 0x0046;
const word grClock = 0x0047;
const word grSecurity = 0x0045;
// Event Params : control codes
const word CodeControlUnknown = 0x0000;
const word CodeControlColdStart = 0x0001;
const word CodeControlWarmStart = 0x0002;
const word CodeControlStop = 0x0003;
const word CodeControlCompress = 0x0004;
const word CodeControlCpyRamRom = 0x0005;
const word CodeControlInsDel = 0x0006;
// Event Result
const word evrNoError = 0x0000;
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;
typedef struct{
uint64_t EvtTime; // Timestamp, was time_t
int EvtSender; // Sender
longword EvtCode; // Event code
word EvtRetCode; // Event result
word EvtParam1; // Param 1 (if available)
word EvtParam2; // Param 2 (if available)
word EvtParam3; // Param 3 (if available)
word EvtParam4; // Param 4 (if available)
}TSrvEvent, *PSrvEvent;
// Server Events callback
typedef void (S7API *pfn_SrvCallBack)(void *usrPtr, PSrvEvent PEvent, int Size);
// Server Read/Write callback
typedef int(S7API *pfn_RWAreaCallBack)(void *usrPtr, int Sender, int Operation, PS7Tag PTag, void *pUsrData);
S7Object S7API Srv_Create();
void S7API Srv_Destroy(S7Object *Server);
int S7API Srv_GetParam(S7Object Server, int ParamNumber, void *pValue);
int S7API Srv_SetParam(S7Object Server, int ParamNumber, void *pValue);
int S7API Srv_StartTo(S7Object Server, const char *Address);
int S7API Srv_Start(S7Object Server);
int S7API Srv_Stop(S7Object Server);
int S7API Srv_RegisterArea(S7Object Server, int AreaCode, word Index, void *pUsrData, int Size);
int S7API Srv_UnregisterArea(S7Object Server, int AreaCode, word Index);
int S7API Srv_LockArea(S7Object Server, int AreaCode, word Index);
int S7API Srv_UnlockArea(S7Object Server, int AreaCode, word Index);
int S7API Srv_GetStatus(S7Object Server, int *ServerStatus, int *CpuStatus, int *ClientsCount);
int S7API Srv_SetCpuStatus(S7Object Server, int CpuStatus);
int S7API Srv_ClearEvents(S7Object Server);
int S7API Srv_PickEvent(S7Object Server, TSrvEvent *pEvent, int *EvtReady);
int S7API Srv_GetMask(S7Object Server, int MaskKind, longword *Mask);
int S7API Srv_SetMask(S7Object Server, int MaskKind, longword Mask);
int S7API Srv_SetEventsCallback(S7Object Server, pfn_SrvCallBack pCallback, void *usrPtr);
int S7API Srv_SetReadEventsCallback(S7Object Server, pfn_SrvCallBack pCallback, void *usrPtr);
int S7API Srv_SetRWAreaCallback(S7Object Server, pfn_RWAreaCallBack pCallback, void *usrPtr);
int S7API Srv_EventText(TSrvEvent *Event, char *Text, int TextLen);
int S7API Srv_ErrorText(int Error, char *Text, int TextLen);
//******************************************************************************
// PARTNER
//******************************************************************************
// Status
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 : linked
const int par_sending = 4; // sending data
const int par_receiving = 5; // receiving data
const int par_binderror = 6; // error starting passive server
// Errors
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
// Brecv Data incoming Callback
typedef void (S7API *pfn_ParRecvCallBack)(void * usrPtr, int opResult, longword R_ID, void *pData, int Size);
// BSend Completion Callback
typedef void (S7API *pfn_ParSendCompletion)(void * usrPtr, int opResult);
S7Object S7API Par_Create(int Active);
void S7API Par_Destroy(S7Object *Partner);
int S7API Par_GetParam(S7Object Partner, int ParamNumber, void *pValue);
int S7API Par_SetParam(S7Object Partner, int ParamNumber, void *pValue);
int S7API Par_StartTo(S7Object Partner, const char *LocalAddress, const char *RemoteAddress,
word LocTsap, word RemTsap);
int S7API Par_Start(S7Object Partner);
int S7API Par_Stop(S7Object Partner);
// BSend
int S7API Par_BSend(S7Object Partner, longword R_ID, void *pUsrData, int Size);
int S7API Par_AsBSend(S7Object Partner, longword R_ID, void *pUsrData, int Size);
int S7API Par_CheckAsBSendCompletion(S7Object Partner, int *opResult);
int S7API Par_WaitAsBSendCompletion(S7Object Partner, longword Timeout);
int S7API Par_SetSendCallback(S7Object Partner, pfn_ParSendCompletion pCompletion, void *usrPtr);
// BRecv
int S7API Par_BRecv(S7Object Partner, longword *R_ID, void *pData, int *Size, longword Timeout);
int S7API Par_CheckAsBRecvCompletion(S7Object Partner, int *opResult, longword *R_ID,
void *pData, int *Size);
int S7API Par_SetRecvCallback(S7Object Partner, pfn_ParRecvCallBack pCompletion, void *usrPtr);
// Stat
int S7API Par_GetTimes(S7Object Partner, longword *SendTime, longword *RecvTime);
int S7API Par_GetStats(S7Object Partner, longword *BytesSent, longword *BytesRecv,
longword *SendErrors, longword *RecvErrors);
int S7API Par_GetLastError(S7Object Partner, int *LastError);
int S7API Par_GetStatus(S7Object Partner, int *Status);
int S7API Par_ErrorText(int Error, char *Text, int TextLen);
#pragma pack()
#ifdef __cplusplus
}
#endif // __cplusplus
#ifdef __cplusplus
//******************************************************************************
// CLIENT CLASS DEFINITION
//******************************************************************************
class TS7Client
{
private:
S7Object Client;
public:
TS7Client();
~TS7Client();
// Control functions
int Connect();
int ConnectTo(const char *RemAddress, int Rack, int Slot);
int SetConnectionParams(const char *RemAddress, word LocalTSAP, word RemoteTSAP);
int SetConnectionType(word ConnectionType);
int Disconnect();
int GetParam(int ParamNumber, void *pValue);
int SetParam(int ParamNumber, void *pValue);
// Data I/O Main 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 Lean 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(s7tm *DateTime);
int SetPlcDateTime(s7tm *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);
// Security functions
int GetProtection(PS7Protection pUsrData);
int SetSessionPassword(char *Password);
int ClearSessionPassword();
// Properties
int ExecTime();
int LastError();
int PDURequested();
int PDULength();
int PlcStatus();
bool Connected();
// Async functions
int SetAsCallback(pfn_CliCompletion pCompletion, void *usrPtr);
bool CheckAsCompletion(int *opResult);
int WaitAsCompletion(longword 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 TS7Client *PS7Client;
//******************************************************************************
// SERVER CLASS DEFINITION
//******************************************************************************
class TS7Server
{
private:
S7Object Server;
public:
TS7Server();
~TS7Server();
// Control
int Start();
int StartTo(const char *Address);
int Stop();
int GetParam(int ParamNumber, void *pValue);
int SetParam(int ParamNumber, void *pValue);
// Events
int SetEventsCallback(pfn_SrvCallBack PCallBack, void *UsrPtr);
int SetReadEventsCallback(pfn_SrvCallBack PCallBack, void *UsrPtr);
int SetRWAreaCallback(pfn_RWAreaCallBack PCallBack, void *UsrPtr);
bool PickEvent(TSrvEvent *pEvent);
void ClearEvents();
longword GetEventsMask();
longword GetLogMask();
void SetEventsMask(longword Mask);
void SetLogMask(longword Mask);
// Resources
int RegisterArea(int AreaCode, word Index, void *pUsrData, word Size);
int UnregisterArea(int AreaCode, word Index);
int LockArea(int AreaCode, word Index);
int UnlockArea(int AreaCode, word Index);
// Properties
int ServerStatus();
int GetCpuStatus();
int SetCpuStatus(int Status);
int ClientsCount();
};
typedef TS7Server *PS7Server;
//******************************************************************************
// PARTNER CLASS DEFINITION
//******************************************************************************
class TS7Partner
{
private:
S7Object Partner; // Partner Handle
public:
TS7Partner(bool Active);
~TS7Partner();
// Control
int GetParam(int ParamNumber, void *pValue);
int SetParam(int ParamNumber, void *pValue);
int Start();
int StartTo(const char *LocalAddress,
const char *RemoteAddress,
int LocalTSAP,
int RemoteTSAP);
int Stop();
// Data I/O functions : BSend
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_ParSendCompletion pCompletion, void *usrPtr);
// Data I/O functions : BRecv
int BRecv(longword *R_ID, void *pUsrData, int *Size, longword Timeout);
bool CheckAsBRecvCompletion(int *opResult, longword *R_ID, void *pUsrData, int *Size);
int SetRecvCallback(pfn_ParRecvCallBack pCallback, void *usrPtr);
// Properties
int Status();
int LastError();
int GetTimes(longword *SendTime, longword *RecvTime);
int GetStats(longword *BytesSent,
longword *BytesRecv,
longword *ErrSend,
longword *ErrRecv);
bool Linked();
};
typedef TS7Partner *PS7Partner;
//******************************************************************************
// OpenPLC interface
//******************************************************************************
void initializeSnap7();
void finalizeSnap7();
#endif // __cplusplus
#endif // snap7_h

View File

@@ -37,6 +37,10 @@
#include "ethercat_src.h"
#endif
#ifdef _snap7
#include "oplc_snap7.h"
#endif
#define OPLC_CYCLE 50000000
extern int opterr;
@@ -103,6 +107,7 @@ int main(int argc,char **argv)
#endif
initializeHardware();
initializeMB();
updateBuffersIn();
updateBuffersOut();
@@ -114,7 +119,11 @@ int main(int argc,char **argv)
readPersistentStorage();
//pthread_t persistentThread;
//pthread_create(&persistentThread, NULL, persistentStorage, NULL);
#ifdef _snap7
initializeSnap7();
#endif
#ifdef __linux__
@@ -243,6 +252,11 @@ int main(int argc,char **argv)
#ifdef _ethercat_src
ethercat_terminate_src();
#endif
#ifdef _snap7
finalizeSnap7();
#endif
printf("Disabling outputs\n");
disableOutputs();
updateBuffersOut();

View File

@@ -138,7 +138,7 @@ void mapUnusedIO()
for (int i = 0; i <= MAX_16B_RANGE; i++)
{
if (i < MIN_16B_RANGE)
if (i < MIN_16B_RANGE)
{
if (int_output[i] == NULL)
{
@@ -339,7 +339,7 @@ void ReadHoldingRegisters(unsigned char *buffer, int bufferSize)
for(int i = 0; i < WordDataLength; i++)
{
int position = Start + i;
if (position <= MIN_16B_RANGE)
if (position < MIN_16B_RANGE)
{
if (int_output[position] != NULL)
{
@@ -575,7 +575,7 @@ void WriteCoil(unsigned char *buffer, int bufferSize)
int writeToRegisterWithoutLocking(int position, uint16_t value)
{
//analog outputs
if (position <= MIN_16B_RANGE)
if (position < MIN_16B_RANGE)
{
if (int_output[position] != NULL) *int_output[position] = value;
}

View File

@@ -134,7 +134,7 @@ login_body = """
<button>login</button>
</form>
</div>
<h3 style="font-family:'roboto', sans-serif; font-size:14px; color:#ffffff;">Release: 2025-02-06</h3>
<h3 style="font-family:'roboto', sans-serif; font-size:14px; color:#ffffff;">Release: 2025-03-17</h3>
</div>
</div>
</body>

View File

@@ -10,6 +10,7 @@ cd scripts &>/dev/null
OPENPLC_PLATFORM=$(cat openplc_platform)
ETHERCAT_OPT=$(cat ethercat)
OPENPLC_DRIVER=$(cat openplc_driver)
S7_OPT=$(cat s7protocol)
#store the active program filename
echo "$1" > ../active_program
@@ -29,6 +30,27 @@ if [ "$ETHERCAT_OPT" = "ethercat" ]; then
sed -i '7s/^/#include "ethercat_src.h" /' Res0.c
fi
#check for S7 Protocol option
if [ "$S7_OPT" = "s7protocol" ]; then
S7_LIB="-lsnap7"
S7_DEF="-D _snap7"
S7_WLIB="snap7.lib"
cp -f ../utils/snap7_src/wrapper/oplc_snap7.* ./core
if [ "$OPENPLC_PLATFORM" = "win" ]; then
cp -f ../utils/snap7_src/build/bin/win64/snap7.* ./core
fi
echo "Including Siemens S7 Protocol via snap7"
else
S7_LIB=""
S7_DEF=""
S7_WLIB=""
# remove snap7 references (if any)
rm -f ./core/oplc_snap7.*
if [ "$OPENPLC_PLATFORM" = "win" ]; then
rm -f ./core/snap7.*
fi
fi
echo "Moving Files..."
mv -f POUS.c POUS.h LOCATED_VARIABLES.h VARIABLES.csv Config0.c Config0.h Res0.c ./core/
if [ $? -ne 0 ]; then
@@ -64,7 +86,7 @@ if [ "$OPENPLC_PLATFORM" = "win" ]; then
echo "Generating glueVars..."
./glue_generator
echo "Compiling main program..."
g++ *.cpp *.o -o openplc -I ./lib -pthread -fpermissive -I /usr/local/include/modbus -L /usr/local/lib -lmodbus -w
g++ *.cpp *.o -o openplc -I ./lib -pthread -fpermissive -I /usr/local/include/modbus -L /usr/local/lib $S7_WLIB -lmodbus -w $S7_DEF
if [ $? -ne 0 ]; then
echo "Error compiling C files"
echo "Compilation finished with errors!"
@@ -77,9 +99,9 @@ elif [ "$OPENPLC_PLATFORM" = "linux" ]; then
echo "Compiling for Linux"
echo "Generating object files..."
if [ "$OPENPLC_DRIVER" = "sl_rp4" ]; then
g++ -std=gnu++11 -I ./lib -c Config0.c -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w -DSL_RP4
g++ -std=gnu++11 -I ./lib -c Config0.c $S7_LIB -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w -DSL_RP4
else
g++ -std=gnu++11 -I ./lib -c Config0.c -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w
g++ -std=gnu++11 -I ./lib -c Config0.c $S7_LIB -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w
fi
if [ $? -ne 0 ]; then
echo "Error compiling C files"
@@ -87,9 +109,9 @@ elif [ "$OPENPLC_PLATFORM" = "linux" ]; then
exit 1
fi
if [ "$OPENPLC_DRIVER" = "sl_rp4" ]; then
g++ -std=gnu++11 -I ./lib -c Res0.c -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w $ETHERCAT_INC -DSL_RP4
g++ -std=gnu++11 -I ./lib -c Res0.c $S7_LIB -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w $ETHERCAT_INC $S7_DEF -DSL_RP4
else
g++ -std=gnu++11 -I ./lib -c Res0.c -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w $ETHERCAT_INC
g++ -std=gnu++11 -I ./lib -c Res0.c $S7_LIB -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w $ETHERCAT_INC $S7_DEF
fi
if [ $? -ne 0 ]; then
echo "Error compiling C files"
@@ -100,9 +122,9 @@ elif [ "$OPENPLC_PLATFORM" = "linux" ]; then
./glue_generator
echo "Compiling main program..."
if [ "$OPENPLC_DRIVER" = "sl_rp4" ]; then
g++ -std=gnu++11 *.cpp *.o -o openplc -I ./lib -pthread -fpermissive `pkg-config --cflags --libs libmodbus` -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w $ETHERCAT_INC -DSL_RP4
g++ -std=gnu++11 *.cpp *.o -o openplc -I ./lib -pthread -fpermissive `pkg-config --cflags --libs libmodbus` $S7_LIB -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w $ETHERCAT_INC $S7_DEF -DSL_RP4
else
g++ -std=gnu++11 *.cpp *.o -o openplc -I ./lib -pthread -fpermissive `pkg-config --cflags --libs libmodbus` -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w $ETHERCAT_INC
g++ -std=gnu++11 *.cpp *.o -o openplc -I ./lib -pthread -fpermissive `pkg-config --cflags --libs libmodbus` $S7_LIB -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w $ETHERCAT_INC $S7_DEF
fi
if [ $? -ne 0 ]; then
echo "Error compiling C files"
@@ -116,9 +138,9 @@ elif [ "$OPENPLC_PLATFORM" = "rpi" ]; then
echo "Compiling for Raspberry Pi"
echo "Generating object files..."
if [ "$OPENPLC_DRIVER" = "sequent" ]; then
g++ -std=gnu++11 -I ./lib -c Config0.c -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w -DSEQUENT
g++ -std=gnu++11 -I ./lib -c Config0.c $S7_LIB -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w -DSEQUENT
else
g++ -std=gnu++11 -I ./lib -c Config0.c -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w
g++ -std=gnu++11 -I ./lib -c Config0.c $S7_LIB -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w
fi
if [ $? -ne 0 ]; then
echo "Error compiling C files"
@@ -126,9 +148,9 @@ elif [ "$OPENPLC_PLATFORM" = "rpi" ]; then
exit 1
fi
if [ "$OPENPLC_DRIVER" = "sequent" ]; then
g++ -std=gnu++11 -I ./lib -c Res0.c -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w -DSEQUENT
g++ -std=gnu++11 -I ./lib -c Res0.c $S7_LIB -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w -DSEQUENT
else
g++ -std=gnu++11 -I ./lib -c Res0.c -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w
g++ -std=gnu++11 -I ./lib -c Res0.c $S7_LIB -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w
fi
if [ $? -ne 0 ]; then
echo "Error compiling C files"
@@ -139,9 +161,9 @@ elif [ "$OPENPLC_PLATFORM" = "rpi" ]; then
./glue_generator
echo "Compiling main program..."
if [ "$OPENPLC_DRIVER" = "sequent" ]; then
g++ -DSEQUENT -std=gnu++11 *.cpp *.o -o openplc -I ./lib -lrt -lpigpio -lpthread -fpermissive `pkg-config --cflags --libs libmodbus` -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w
g++ -DSEQUENT -std=gnu++11 *.cpp *.o -o openplc -I ./lib -lrt -lpigpio -lpthread -fpermissive `pkg-config --cflags --libs libmodbus` $S7_LIB -lasiodnp3 -lasiopal -lopendnp3 -lopenpal $S7_DEF -w
else
g++ -std=gnu++11 *.cpp *.o -o openplc -I ./lib -lrt -lpigpio -lpthread -fpermissive `pkg-config --cflags --libs libmodbus` -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w
g++ -std=gnu++11 *.cpp *.o -o openplc -I ./lib -lrt -lpigpio -lpthread -fpermissive `pkg-config --cflags --libs libmodbus` $S7_LIB -lasiodnp3 -lasiopal -lopendnp3 -lopenpal $S7_DEF -w
fi
if [ $? -ne 0 ]; then
echo "Error compiling C files"
@@ -155,13 +177,13 @@ elif [ "$OPENPLC_PLATFORM" = "opi" ]; then
WIRINGOP_INC="-I/usr/local/include -L/usr/local/lib -lwiringPi -lwiringPiDev"
echo "Compiling for Orange Pi"
echo "Generating object files..."
g++ -std=gnu++11 -I ./lib -c Config0.c -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w $WIRINGOP_INC
g++ -std=gnu++11 -I ./lib -c Config0.c $S7_LIB -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w $WIRINGOP_INC
if [ $? -ne 0 ]; then
echo "Error compiling C files"
echo "Compilation finished with errors!"
exit 1
fi
g++ -std=gnu++11 -I ./lib -c Res0.c -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w $WIRINGOP_INC
g++ -std=gnu++11 -I ./lib -c Res0.c $S7_LIB -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w $WIRINGOP_INC
if [ $? -ne 0 ]; then
echo "Error compiling C files"
echo "Compilation finished with errors!"
@@ -170,7 +192,7 @@ elif [ "$OPENPLC_PLATFORM" = "opi" ]; then
echo "Generating glueVars..."
./glue_generator
echo "Compiling main program..."
g++ -std=gnu++11 *.cpp *.o -o openplc -I ./lib -lrt -lpthread -fpermissive `pkg-config --cflags --libs libmodbus` -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w $WIRINGOP_INC
g++ -std=gnu++11 *.cpp *.o -o openplc -I ./lib -lrt -lpthread -fpermissive `pkg-config --cflags --libs libmodbus` $S7_LIB -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w $S7_DEF $WIRINGOP_INC
if [ $? -ne 0 ]; then
echo "Error compiling C files"
echo "Compilation finished with errors!"

View File

@@ -0,0 +1 @@
#s7protocol