44_S7_S7Client.pm 33 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337
  1. # $Id: 44_S7_S7Client.pm 15511 2017-11-27 21:13:16Z charlie71 $
  2. ##############################################
  3. use strict;
  4. use warnings;
  5. require Exporter;
  6. use Config;
  7. use AutoLoader;
  8. require "44_S7_Client.pm" ;
  9. #use Socket;
  10. use IO::Socket::INET;
  11. use IO::Select;
  12. #todo
  13. #fehler in settimino:
  14. #function :WriteArea & ReadArea
  15. #bit shift opteratin in wrong direction
  16. # PDU.H[23]=NumElements<<8; --> PDU.H[23]=NumElements>>8;
  17. # PDU.H[24]=NumElements;
  18. our @ISA = qw(Exporter);
  19. our %EXPORT_TAGS = (
  20. 'all' => [
  21. qw(
  22. errTCPConnectionFailed
  23. errTCPConnectionReset
  24. errTCPDataRecvTout
  25. errTCPDataSend
  26. errTCPDataRecv
  27. errISOConnectionFailed
  28. errISONegotiatingPDU
  29. errISOInvalidPDU
  30. errS7InvalidPDU
  31. errS7SendingPDU
  32. errS7DataRead
  33. errS7DataWrite
  34. errS7Function
  35. errBufferTooSmall
  36. Code7Ok
  37. Code7AddressOutOfRange
  38. Code7InvalidTransportSize
  39. Code7WriteDataSizeMismatch
  40. Code7ResItemNotAvailable
  41. Code7ResItemNotAvailable1
  42. Code7InvalidValue
  43. Code7NeedPassword
  44. Code7InvalidPassword
  45. Code7NoPasswordToClear
  46. Code7NoPasswordToSet
  47. Code7FunNotAvailable
  48. Code7DataOverPDU
  49. S7_PG
  50. S7_OP
  51. S7_Basic
  52. ISOSize
  53. isotcp
  54. MinPduSize
  55. MaxPduSize
  56. CC
  57. S7Shift
  58. S7WLBit
  59. S7WLByte
  60. S7WLWord
  61. S7WLDWord
  62. S7WLReal
  63. S7WLCounter
  64. S7WLTimer
  65. S7CpuStatusUnknown
  66. S7CpuStatusRun
  67. S7CpuStatusStop
  68. RxOffset
  69. Size_RD
  70. Size_WR
  71. Size_DT
  72. )
  73. ]
  74. );
  75. our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
  76. our @EXPORT = qw(
  77. errTCPConnectionFailed
  78. errTCPConnectionReset
  79. errTCPDataRecvTout
  80. errTCPDataSend
  81. errTCPDataRecv
  82. errISOConnectionFailed
  83. errISONegotiatingPDU
  84. errISOInvalidPDU
  85. errS7InvalidPDU
  86. errS7SendingPDU
  87. errS7DataRead
  88. errS7DataWrite
  89. errS7Function
  90. errBufferTooSmall
  91. Code7Ok
  92. Code7AddressOutOfRange
  93. Code7InvalidTransportSize
  94. Code7WriteDataSizeMismatch
  95. Code7ResItemNotAvailable
  96. Code7ResItemNotAvailable1
  97. Code7InvalidValue
  98. Code7NeedPassword
  99. Code7InvalidPassword
  100. Code7NoPasswordToClear
  101. Code7NoPasswordToSet
  102. Code7FunNotAvailable
  103. Code7DataOverPDU
  104. S7_PG
  105. S7_OP
  106. S7_Basic
  107. ISOSize
  108. isotcp
  109. MinPduSize
  110. MaxPduSize
  111. CC
  112. S7Shift
  113. S7WLBit
  114. S7WLByte
  115. S7WLWord
  116. S7WLDWord
  117. S7WLReal
  118. S7WLCounter
  119. S7WLTimer
  120. S7CpuStatusUnknown
  121. S7CpuStatusRun
  122. S7CpuStatusStop
  123. RxOffset
  124. Size_RD
  125. Size_WR
  126. Size_DT
  127. );
  128. package S7Client;
  129. use strict;
  130. #use S7ClientBase;
  131. our @ISA = qw(S7ClientBase); # inherits from Person
  132. # Error Codes
  133. # from 0x0001 up to 0x00FF are severe errors, the Client should be disconnected
  134. # from 0x0100 are S7 Errors such as DB not found or address beyond the limit etc..
  135. # For Arduino Due the error code is a 32 bit integer but this doesn't change the constants use.
  136. use constant errTCPConnectionFailed => 0x0001;
  137. use constant errTCPConnectionReset => 0x0002;
  138. use constant errTCPDataRecvTout => 0x0003;
  139. use constant errTCPDataSend => 0x0004;
  140. use constant errTCPDataRecv => 0x0005;
  141. use constant errISOConnectionFailed => 0x0006;
  142. use constant errISONegotiatingPDU => 0x0007;
  143. use constant errISOInvalidPDU => 0x0008;
  144. use constant errS7InvalidPDU => 0x0100;
  145. use constant errS7SendingPDU => 0x0200;
  146. use constant errS7DataRead => 0x0300;
  147. use constant errS7DataWrite => 0x0400;
  148. use constant errS7Function => 0x0500;
  149. use constant errBufferTooSmall => 0x0600;
  150. #CPU Errors
  151. # S7 outcoming Error code
  152. use constant Code7Ok => 0x0000;
  153. use constant Code7AddressOutOfRange => 0x0005;
  154. use constant Code7InvalidTransportSize => 0x0006;
  155. use constant Code7WriteDataSizeMismatch => 0x0007;
  156. use constant Code7ResItemNotAvailable => 0x000A;
  157. use constant Code7ResItemNotAvailable1 => 0xD209;
  158. use constant Code7InvalidValue => 0xDC01;
  159. use constant Code7NeedPassword => 0xD241;
  160. use constant Code7InvalidPassword => 0xD602;
  161. use constant Code7NoPasswordToClear => 0xD604;
  162. use constant Code7NoPasswordToSet => 0xD605;
  163. use constant Code7FunNotAvailable => 0x8104;
  164. use constant Code7DataOverPDU => 0x8500;
  165. # Connection Type
  166. use constant S7_PG => 0x01;
  167. use constant S7_OP => 0x02;
  168. use constant S7_Basic => 0x03;
  169. # ISO and PDU related constants
  170. use constant ISOSize => 7; # Size of TPKT + COTP Header
  171. use constant isotcp => 102; # ISOTCP Port
  172. use constant MinPduSize => 16; # Minimum S7 valid telegram size
  173. use constant MaxPduSize =>
  174. 247; # Maximum S7 valid telegram size (we negotiate 240 bytes + ISOSize)
  175. use constant CC => 0xD0; # Connection confirm
  176. use constant S7Shift =>
  177. 17; # We receive data 17 bytes above to align with PDU.DATA[]
  178. # WordLength
  179. use constant S7WLBit => 0x01;
  180. use constant S7WLByte => 0x02;
  181. use constant S7WLChar => 0x03;
  182. use constant S7WLWord => 0x04;
  183. use constant S7WLInt => 0x05;
  184. use constant S7WLDWord => 0x06;
  185. use constant S7WLDInt => 0x07;
  186. use constant S7WLReal => 0x08;
  187. use constant S7WLCounter => 0x1C;
  188. use constant S7WLTimer => 0x1D;
  189. # Result transport size
  190. use constant TS_ResBit => 0x03;
  191. use constant TS_ResByte => 0x04;
  192. use constant TS_ResInt => 0x05;
  193. use constant TS_ResReal => 0x07;
  194. use constant TS_ResOctet => 0x09;
  195. use constant S7CpuStatusUnknown => 0x00;
  196. use constant S7CpuStatusRun => 0x08;
  197. use constant S7CpuStatusStop => 0x04;
  198. use constant RxOffset => 18;
  199. use constant Size_DT => 25;
  200. use constant Size_RD => 31;
  201. use constant Size_WR => 35;
  202. sub new {
  203. my $class = shift;
  204. my $self = $class->SUPER::new();
  205. $self->{LocalTSAP_HI} = 0x01;
  206. $self->{LocalTSAP_LO} = 0x00;
  207. $self->{RemoteTSAP_HI} = 0x01;
  208. $self->{RemoteTSAP_LO} = 0x02;
  209. $self->{ConnType} = &S7_PG;
  210. $self->{LastError} = 0;
  211. $self->{LastPDUType} = 0;
  212. $self->{Peer} = "";
  213. $self->{ISO_CR} = "";
  214. $self->{S7_PN} = "";
  215. $self->{S7_RW} = "";
  216. $self->{PDU} = {};
  217. $self->{cntword} = 0;
  218. #ISO Connection Request telegram (contains also ISO Header and COTP Header)
  219. $self->{ISO_CR} = pack(
  220. "C22",
  221. # TPKT (RFC1006 Header)
  222. 0x03, # RFC 1006 ID (3)
  223. 0x00, # Reserved, always 0
  224. 0x00
  225. , # High part of packet length (entire frame, payload and TPDU included)
  226. 0x16
  227. , # Low part of packet length (entire frame, payload and TPDU included)
  228. # COTP (ISO 8073 Header)
  229. 0x11, # PDU Size Length
  230. 0xE0, # CR - Connection Request ID
  231. 0x00, # Dst Reference HI
  232. 0x00, # Dst Reference LO
  233. 0x00, # Src Reference HI
  234. 0x01, # Src Reference LO
  235. 0x00, # Class + Options Flags
  236. 0xC0, # PDU Max Length ID
  237. 0x01, # PDU Max Length HI
  238. 0x0A, # PDU Max Length LO # snap7 value Bytes 1024
  239. # 0x09, # PDU Max Length LO # libnodave value Bytes 512
  240. 0xC1, # Src TSAP Identifier
  241. 0x02, # Src TSAP Length (2 bytes)
  242. 0x01, # Src TSAP HI (will be overwritten by ISOConnect())
  243. 0x00, # Src TSAP LO (will be overwritten by ISOConnect())
  244. 0xC2, # Dst TSAP Identifier
  245. 0x02, # Dst TSAP Length (2 bytes)
  246. 0x01, # Dst TSAP HI (will be overwritten by ISOConnect())
  247. 0x02 # Dst TSAP LO (will be overwritten by ISOConnect())
  248. );
  249. # S7 PDU Negotiation Telegram (contains also ISO Header and COTP Header)
  250. $self->{S7_PN} = pack(
  251. "C25",
  252. 0x03, 0x00, 0x00, 0x19, 0x02, 0xf0,
  253. 0x80, # TPKT + COTP (see above for info)
  254. 0x32, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, #snap7 trace
  255. 0x00, 0xf0, 0x00, 0x00, 0x01, 0x00, 0x01,
  256. # 0x00, 0xf0 # PDU Length Requested = HI-LO 240 bytes
  257. # 0x01, 0xe0 # PDU Length Requested = HI-LO 480 bytes
  258. 0x03, 0xc0 # PDU Length Requested = HI-LO 960 bytes
  259. );
  260. # S7 Read/Write Request Header (contains also ISO Header and COTP Header)
  261. $self->{S7_RW} = pack(
  262. "C35", # 31-35 bytes
  263. 0x03, 0x00,
  264. 0x00, 0x1f, # Telegram Length (Data Size + 31 or 35)
  265. 0x02, 0xf0, 0x80, # COTP (see above for info)
  266. 0x32, # S7 Protocol ID
  267. 0x01, # Job Type
  268. 0x00, 0x00, # Redundancy identification (AB_EX)
  269. 0x05, 0x00, # PDU Reference #snap7 (increment by every read/write)
  270. 0x00, 0x0e, # Parameters Length
  271. 0x00, 0x00, # Data Length = Size(bytes) + 4
  272. 0x04, # Function 4 Read Var, 5 Write Var
  273. #reqest param head
  274. 0x01, # Items count
  275. 0x12, # Var spec.
  276. 0x0a, # Length of remaining bytes
  277. 0x10, # Syntax ID
  278. &S7WLByte, # Transport Size
  279. 0x00, 0x00, # Num Elements
  280. 0x00, 0x00, # DB Number (if any, else 0)
  281. 0x84, # Area Type
  282. 0x00, 0x00, 0x00, # Area Offset
  283. # WR area
  284. 0x00, # Reserved
  285. 0x04, # Transport size
  286. 0x00, 0x00, # Data Length * 8 (if not timer or counter)
  287. );
  288. $self->{PDU}->{H} = pack( "C35",
  289. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  290. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  291. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  292. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 );
  293. $self->{PDU}->{DATA} = "";
  294. $self->{TCPClient} = undef;
  295. return bless $self, $class;
  296. }
  297. #-----------------------------------------------------------------------------
  298. sub GetNextWord {
  299. my $self = shift;
  300. $self->{cntword} = 0 if ( $self->{cntword} == 0xFFFF );
  301. return $self->{cntword}++;
  302. }
  303. #-----------------------------------------------------------------------------
  304. sub SetLastError {
  305. my ( $self, $Error ) = @_;
  306. $self->{LastError} = $Error;
  307. return $Error;
  308. }
  309. #-----------------------------------------------------------------------------
  310. sub WaitForData {
  311. my ( $self, $Size, $Timeout ) = @_;
  312. my $BytesReady;
  313. $Timeout = $Timeout / 1000;
  314. # $Timeout = 1 if ($Timeout < 1); #deactivated in V2.9
  315. my @ready = $self->{TCPClientSel}->can_read($Timeout);
  316. if ( scalar(@ready) ) {
  317. return $self->SetLastError(0);
  318. }
  319. # Here we are in timeout zone, if there's something into the buffer, it must be discarded.
  320. $self->{TCPClient}->flush();
  321. if ( !$self->{TCPClient}->connected() ) {
  322. return $self->SetLastError(&errTCPConnectionReset);
  323. }
  324. return $self->SetLastError(&errTCPDataRecvTout);
  325. }
  326. #-----------------------------------------------------------------------------
  327. sub IsoPduSize {
  328. my ($self) = @_;
  329. my @buffer = unpack( "C" x 4, $self->{PDU}->{H} );
  330. my $Size = $buffer[2];
  331. return ( $Size << 8 ) + $buffer[3];
  332. }
  333. #-----------------------------------------------------------------------------
  334. sub RecvPacket {
  335. my ( $self, $Size ) = @_;
  336. my $buf;
  337. $self->WaitForData( $Size, $self->{RecvTimeout} );
  338. if ( $self->{LastError} != 0 ) {
  339. return $self->{LastError};
  340. }
  341. my $res = $self->{TCPClient}->recv( $buf, $Size );
  342. if ( defined($buf) && length($buf) == $Size ) {
  343. return ( $self->SetLastError(0), $buf );
  344. }
  345. else {
  346. if ( defined($buf) ) {
  347. if ( $main::attr{global}{verbose} <= 3 ) {
  348. my $b = join( ", ", unpack( "H2 " x length($buf), $buf ) );
  349. main::Log3 (undef, 3, "TCPClient RecvPacket error (IP= ". $self->{Peer} . "): " . $b);
  350. }
  351. }
  352. else {
  353. main::Log3 (undef, 3, "TCPClient RecvPacket error (IP= " . $self->{Peer} . ").");
  354. }
  355. return $self->SetLastError( &errTCPConnectionReset, $buf );
  356. }
  357. }
  358. #-----------------------------------------------------------------------------
  359. sub SetConnectionParams {
  360. my ( $self, $Address, $LocalTSAP, $RemoteTSAP ) = @_;
  361. $self->{Peer} = $Address;
  362. $self->{LocalTSAP_HI} = $LocalTSAP >> 8;
  363. $self->{LocalTSAP_LO} = $LocalTSAP & 0x00FF;
  364. $self->{RemoteTSAP_HI} = $RemoteTSAP >> 8;
  365. $self->{RemoteTSAP_LO} = $RemoteTSAP & 0x00FF;
  366. }
  367. #-----------------------------------------------------------------------------
  368. sub SetConnectionType {
  369. my ( $self, $ConnectionType ) = @_;
  370. $self->{ConnType} = $ConnectionType;
  371. }
  372. #-----------------------------------------------------------------------------
  373. sub ConnectTo {
  374. my ( $self, $Address, $Rack, $Slot ) = @_;
  375. $self->SetConnectionParams( $Address, 0x0100,
  376. ( $self->{ConnType} << 8 ) + ( $Rack * 0x20 ) + $Slot );
  377. return $self->Connect();
  378. }
  379. #-----------------------------------------------------------------------------
  380. sub Connect {
  381. my ($self) = @_;
  382. $self->{LastError} = 0;
  383. if ( !$self->{Connected} ) {
  384. $self->TCPConnect();
  385. if ( $self->{LastError} == 0 ) # First stage : TCP Connection
  386. {
  387. $self->ISOConnect();
  388. if ( $self->{LastError} ==
  389. 0 ) # Second stage : ISOTCP (ISO 8073) Connection
  390. {
  391. $self->{LastError} = $self->NegotiatePduLength()
  392. ; # Third stage : S7 PDU negotiation
  393. }
  394. }
  395. }
  396. if ( $self->{LastError} == 0 ) {
  397. $self->{Connected} = 1;
  398. }
  399. else {
  400. $self->{Connected} = 0;
  401. }
  402. return $self->{LastError};
  403. }
  404. #-----------------------------------------------------------------------------
  405. sub Disconnect {
  406. my ($self) = @_;
  407. if ( $self->{Connected} ) {
  408. $self->{TCPClientSel} = undef;
  409. if ( defined( $self->{TCPClient} ) ) {
  410. my $res = shutdown( $self->{TCPClient}, 1 );
  411. if ( defined($res) ) {
  412. $self->{TCPClient}->flush() if ( $res == 0 );
  413. }
  414. $self->{TCPClient}->close();
  415. $self->{TCPClient} = undef;
  416. }
  417. $self->{Connected} = 0;
  418. $self->{PDULength} = 0;
  419. $self->{MaxReadLength} = 0;
  420. $self->{LastError} = 0;
  421. }
  422. }
  423. #-----------------------------------------------------------------------------
  424. sub TCPConnect {
  425. my ($self) = @_;
  426. # # 1. create a socket handle (descriptor)
  427. # my($sock);
  428. # socket($sock, AF_INET, SOCK_STREAM, IPPROTO_TCP);#TCP_NODELAY,
  429. #
  430. # or die "ERROR in Socket Creation: $!";
  431. #
  432. # # 2. connect to remote server
  433. # my $remote = $self->{Peer};
  434. #
  435. # my $iaddr = inet_aton($remote) or die "Unable to resolve hostname : $remote";
  436. # my $paddr = sockaddr_in(&isotcp, $iaddr); #socket address structure
  437. #
  438. # connect($sock , $paddr) or die "connect to $remote failed : $!";
  439. # $self->{TCPClient} = $sock;
  440. # return $self->SetLastError(0);
  441. #
  442. # $self->{TCPClientSel} = new IO::Select($self->{TCPClient});
  443. $self->{TCPClient} = new IO::Socket::INET(
  444. PeerAddr => $self->{Peer},
  445. # PeerHost => $self->{Peer},
  446. PeerPort => &isotcp,
  447. Type => Socket::SOCK_STREAM, # probably needed on some systems
  448. Proto => 'tcp',
  449. ) or die "ERROR in Socket Creation: $!";
  450. $self->{TCPClient}->sockopt( &Socket::TCP_NODELAY, 1 );
  451. $self->{TCPClient}->autoflush(1);
  452. $self->{TCPClientSel} = new IO::Select( $self->{TCPClient} );
  453. return $self->SetLastError(0);
  454. }
  455. #-----------------------------------------------------------------------------
  456. sub RecvISOPacket {
  457. my ($self) = @_;
  458. my $Size;
  459. my $Done = 0;
  460. my $pdubuffer = "";
  461. my $res;
  462. $self->{LastError} = 0;
  463. while ( ( $self->{LastError} == 0 ) && !$Done ) {
  464. # Get TPKT (4 bytes)
  465. ( $res, $pdubuffer ) = $self->RecvPacket(4);
  466. if ( $self->{LastError} == 0 ) {
  467. my $b = join( ", ", unpack( "H2 " x 4, $pdubuffer ) );
  468. $self->{PDU}->{H} = $pdubuffer . substr( $self->{PDU}->{H}, 4 );
  469. $Size = $self->IsoPduSize();
  470. main::Log3(undef, 5, "TCPClient RecvISOPacket Expected Size = $Size");
  471. # Check 0 bytes Data Packet (only TPKT+COTP - 7 bytes)
  472. if ( $Size == 7 ) {
  473. $pdubuffer = "";
  474. ( $res, $pdubuffer ) = $self->RecvPacket(3);
  475. $self->{PDU}->{H} = $pdubuffer . substr( $self->{PDU}->{H}, 3 );
  476. }
  477. else {
  478. my $maxlen = $self->{PDULength} + &ISOSize;
  479. if ( $maxlen <= &MinPduSize ) {
  480. $maxlen = &MaxPduSize;
  481. }
  482. # if (($Size > &MaxPduSize) || ($Size < &MinPduSize)) {
  483. if ( ( $Size > $maxlen ) || ( $Size < &MinPduSize ) ) {
  484. main::Log3 (undef, 3, "TCPClient RecvISOPacket PDU overflow (IP= " . $self->{Peer} . "): size = $Size , maxPDULength = " . $self->{PDULength});
  485. $self->{LastError} = &errISOInvalidPDU;
  486. }
  487. else {
  488. $Done = 1; # a valid Length !=7 && >16 && <247
  489. }
  490. }
  491. }
  492. }
  493. if ( $self->{LastError} == 0 ) {
  494. $pdubuffer = "";
  495. ( $res, $pdubuffer ) = $self->RecvPacket(3);
  496. $self->{PDU}->{H} = $pdubuffer
  497. . substr( $self->{PDU}->{H}, 3 ); # Skip remaining 3 COTP bytes
  498. my @mypdu = unpack( "C2", $self->{PDU}->{H} );
  499. $self->{LastPDUType} = $mypdu[1]; # Stores PDU Type, we need it
  500. $Size -= &ISOSize;
  501. # We need to align with PDU.DATA
  502. $pdubuffer = "";
  503. ( $res, $pdubuffer ) = $self->RecvPacket($Size);
  504. if ( $main::attr{global}{verbose} >= 5 ) {
  505. my $b = join( ", ", unpack( "H2 " x $Size, $pdubuffer ) );
  506. main::Log3 (undef, 5, "TCPClient RecvISOPacket (IP= " . $self->{Peer} . "): $b");
  507. }
  508. #we write the data starting at position 17 (shift) into the PDU.H
  509. if ( $self->{LastError} == 0 ) {
  510. if ( $Size > &Size_WR - &S7Shift ) {
  511. my $headerSize = &Size_WR - &S7Shift;
  512. $self->{PDU}->{H} =
  513. substr( $self->{PDU}->{H}, 0, &S7Shift )
  514. . substr( $pdubuffer, 0, $headerSize );
  515. $self->{PDU}->{DATA} = substr( $pdubuffer, $headerSize );
  516. }
  517. else {
  518. $self->{PDU}->{H} =
  519. substr( $self->{PDU}->{H}, 0, &S7Shift )
  520. . $pdubuffer
  521. . substr( $self->{PDU}->{H}, &Size_WR - &S7Shift - $Size );
  522. }
  523. }
  524. }
  525. if ( $self->{LastError} != 0 ) {
  526. $self->{TCPClient}->flush();
  527. }
  528. return ( $self->{LastError}, $Size );
  529. }
  530. #-----------------------------------------------------------------------------
  531. sub ISOConnect {
  532. my ($self) = @_;
  533. my $Done = 0;
  534. my $myLength = 0;
  535. my $res;
  536. # Setup TSAPs
  537. my @myISO_CR = unpack( "C22", $self->{ISO_CR} );
  538. $myISO_CR[16] = $self->{LocalTSAP_HI};
  539. $myISO_CR[17] = $self->{LocalTSAP_LO};
  540. $myISO_CR[20] = $self->{RemoteTSAP_HI};
  541. $myISO_CR[21] = $self->{RemoteTSAP_LO};
  542. $self->{ISO_CR} = pack( "C22", @myISO_CR );
  543. my $b = join( ", ", unpack( "H2 " x 22, $self->{ISO_CR} ) );
  544. if ( $self->{TCPClient}->send( $self->{ISO_CR} ) == 22 )
  545. # if (send($self->{TCPClient}, $self->{ISO_CR}, &MSG_NOSIGNAL)==22)
  546. {
  547. ( $res, $myLength ) = $self->RecvISOPacket();
  548. if ( ( $self->{LastError} == 0 )
  549. && ( $myLength == 15 )
  550. ) # 15 = 22 (sizeof CC telegram) - 7 (sizeof Header)
  551. {
  552. if ( $self->{LastPDUType} == &CC ) { #Connection confirm
  553. return 0;
  554. }
  555. else {
  556. return $self->SetLastError(&errISOInvalidPDU);
  557. }
  558. }
  559. else {
  560. return $self->{LastError};
  561. }
  562. }
  563. else {
  564. return $self->SetLastError(&errISOConnectionFailed);
  565. }
  566. }
  567. #-----------------------------------------------------------------------------
  568. sub NegotiatePduLength {
  569. my ($self) = @_;
  570. my $myLength;
  571. my $res;
  572. # Setup TSAPs
  573. my @myS7_PN = unpack( "C25", $self->{S7_PN} );
  574. my $myPDUID = $self->GetNextWord();
  575. $myS7_PN[11] = $myPDUID % 256;
  576. $myS7_PN[12] = ( $myPDUID >> 8 ) % 256;
  577. $self->{S7_PN} = pack( "C25", @myS7_PN );
  578. if ( $self->{TCPClient}->send( $self->{S7_PN} ) == 25 )
  579. # if (send($self->{TCPClient}, $self->{S7_PN}, &MSG_NOSIGNAL)==25)
  580. {
  581. ( $res, $myLength ) = $self->RecvISOPacket();
  582. if ( $self->{LastError} == 0 ) {
  583. # check S7 Error
  584. my @myPDUheader = unpack( "C35", $self->{PDU}->{H} );
  585. if ( ( $myLength == 20 )
  586. && ( $myPDUheader[27] == 0 )
  587. && ( $myPDUheader[28] == 0 ) ) # 20 = size of Negotiate Answer
  588. {
  589. my @myPDUdata = unpack( "C2", $self->{PDU}->{DATA} );
  590. $self->{PDULength} = $myPDUdata[0];
  591. $self->{PDULength} =
  592. ( $self->{PDULength} << 8 ) +
  593. $myPDUdata[1]; # Value negotiated
  594. $self->{MaxReadLength} = ( $self->{PDULength} - 18 );
  595. if ( $self->{PDULength} > 0 ) {
  596. return 0;
  597. }
  598. else {
  599. return $self->SetLastError(&errISONegotiatingPDU);
  600. }
  601. }
  602. else {
  603. return $self->SetLastError(&errISONegotiatingPDU);
  604. }
  605. }
  606. else {
  607. return $self->{LastError};
  608. }
  609. }
  610. else {
  611. return $self->SetLastError(&errISONegotiatingPDU);
  612. }
  613. }
  614. sub getPDULength() {
  615. my ($self) = @_;
  616. if ( $self->{Connected} ) {
  617. return $self->{PDULength};
  618. }
  619. return -1;
  620. }
  621. #-----------------------------------------------------------------------------
  622. sub ReadArea () {
  623. my ( $self, $Area, $DBNumber, $Start, $Amount, $WordLen ) = @_;
  624. my $ptrData = "";
  625. my $Address;
  626. my $NumElements;
  627. my $MaxElements;
  628. my $TotElements;
  629. my $SizeRequested;
  630. my $myLength;
  631. my $res;
  632. my $WordSize = 1;
  633. $self->{LastError} = 0;
  634. # If we are addressing Timers or counters the element size is 2
  635. $WordSize = 2 if ( ( $Area == &S7ClientBase::S7AreaCT ) || ( $Area == &S7ClientBase::S7AreaTM ) );
  636. $MaxElements =
  637. ( $self->{PDULength} - 18 ) / $WordSize; # 18 = Reply telegram header
  638. $TotElements = $Amount;
  639. while ( ( $TotElements > 0 ) && ( $self->{LastError} == 0 ) ) {
  640. $NumElements = $TotElements;
  641. $NumElements = $MaxElements if ( $NumElements > $MaxElements );
  642. $SizeRequested = $NumElements * $WordSize;
  643. # Setup the telegram
  644. my @myPDU =
  645. unpack( "C" x &Size_RD, substr( $self->{S7_RW}, 0, &Size_RD ) );
  646. #my $b = join( ", ", unpack("H2 " x &Size_RD,$self->{S7_RW}));
  647. # print "ReadArea: S7_RW :".$b."\n";
  648. #set PDU Ref
  649. my $myPDUID = $self->GetNextWord();
  650. $myPDU[11] = $myPDUID % 256;
  651. $myPDU[12] = ( $myPDUID >> 8 ) % 256;
  652. $myPDU[20] = 0x0a; # Length of remaining bytes
  653. $myPDU[21] = 0x10; # syntag ID
  654. # Set DB Number
  655. $myPDU[27] = $Area;
  656. if ( $Area == &S7ClientBase::S7AreaDB ) {
  657. $myPDU[25] = ( $DBNumber >> 8 ) % 256;
  658. $myPDU[26] = $DBNumber % 256;
  659. }
  660. else {
  661. $myPDU[25] = 0x00;
  662. $myPDU[26] = 0x00;
  663. }
  664. # Adjusts Start
  665. if ( ( $WordLen == &S7WLBit )
  666. || ( $WordLen == &S7WLCounter )
  667. || ( $WordLen == &S7WLTimer ) )
  668. {
  669. $Address = $Start;
  670. }
  671. else {
  672. $Address = $Start << 3;
  673. }
  674. #set word length
  675. $myPDU[22] = $WordLen;
  676. # Num elements
  677. $myPDU[23] = ( $NumElements >> 8 )
  678. % 256; # hier ist denke ich ein fehler in der settimino.cpp
  679. $myPDU[24] = ($NumElements) % 256;
  680. # Address into the PLC
  681. $myPDU[30] = ($Address) % 256;
  682. $Address = $Address >> 8;
  683. $myPDU[29] = ($Address) % 256;
  684. $Address = $Address >> 8;
  685. $myPDU[28] = ($Address) % 256;
  686. $self->{PDU}->{H} =
  687. pack( "C" x &Size_RD, @myPDU )
  688. . substr( $self->{PDU}->{H}, &Size_RD );
  689. if ( $main::attr{global}{verbose} >= 5 ) {
  690. $b = join( ", ", unpack( "H2 " x &Size_RD, $self->{PDU}->{H} ) );
  691. main::Log3 (undef, 5, "TCPClient ReadArea (IP= " . $self->{Peer} . "): $b");
  692. }
  693. $b = substr( $self->{PDU}->{H}, 0, &Size_RD );
  694. if ( $self->{TCPClient}->send($b) == &Size_RD )
  695. { #Achtung PDU.H ist größer als &Size_RD
  696. # if (send($self->{TCPClient}, $b, &MSG_NOSIGNAL)== &Size_RD) #Achtung PDU.H ist größer als &Size_RD
  697. ( $res, $myLength ) = $self->RecvISOPacket();
  698. if ( $self->{LastError} == 0 ) {
  699. if ( $myLength >= 18 ) {
  700. @myPDU = unpack( "C" x &Size_WR, $self->{PDU}->{H} );
  701. if ( ( $myLength - 18 == $SizeRequested ) ) {
  702. #response was OK
  703. $ptrData =
  704. substr( $self->{PDU}->{DATA}, 0, $SizeRequested )
  705. ; # Copies in the user's buffer
  706. }
  707. else { # PLC reports an error
  708. if ( $myPDU[31] == 0xFF ) {
  709. my $b = join(
  710. ", ",
  711. unpack(
  712. "H2 " x $myLength,
  713. $self->{PDU}->{H} . $self->{PDU}->{DATA}
  714. )
  715. );
  716. main::Log3 (undef, 3, "TCPClient ReadArea error (IP= " . $self->{Peer}. ") returned data not expected size: $b");
  717. }
  718. else {
  719. my $b = join(
  720. ", ",
  721. unpack(
  722. "H2 " x (
  723. length( $self->{PDU}->{H} ) +
  724. length( $self->{PDU}->{DATA} )
  725. ),
  726. $self->{PDU}->{H} . $self->{PDU}->{DATA}
  727. )
  728. );
  729. main::Log3 (undef, 3,
  730. "TCPClient ReadArea error (IP= "
  731. . $self->{Peer}
  732. . ") returned data not OK: $b");
  733. }
  734. $self->{LastError} = &errS7DataRead;
  735. }
  736. }
  737. else {
  738. $self->{LastError} = &errS7InvalidPDU;
  739. }
  740. }
  741. }
  742. else {
  743. $self->{LastError} = &errTCPDataSend;
  744. }
  745. $TotElements -= $NumElements;
  746. $Start += $NumElements * $WordSize;
  747. }
  748. return ( $self->{LastError}, $ptrData );
  749. }
  750. #-----------------------------------------------------------------------------
  751. sub WriteArea {
  752. my ( $self, $Area, $DBNumber, $Start, $Amount, $WordLen, $ptrData ) = @_;
  753. my $Address;
  754. my $NumElements;
  755. my $MaxElements;
  756. my $TotElements;
  757. my $DataSize;
  758. my $IsoSize;
  759. my $myLength;
  760. my $Offset = 0;
  761. my $WordSize = 1;
  762. my $res;
  763. $self->{LastError} = 0;
  764. # If we are addressing Timers or counters the element size is 2
  765. $WordSize = 2 if ( ( $Area == &S7ClientBase::S7AreaCT ) || ( $Area == &S7ClientBase::S7AreaTM ) );
  766. $MaxElements =
  767. ( $self->{PDULength} - 35 ) / $WordSize; # 35 = Write telegram header
  768. $TotElements = $Amount;
  769. while ( ( $TotElements > 0 ) && ( $self->{LastError} == 0 ) ) {
  770. $NumElements = $TotElements;
  771. if ( $NumElements > $MaxElements ) {
  772. $NumElements = $MaxElements;
  773. }
  774. #If we use the internal buffer only, we cannot exced the PDU limit
  775. $DataSize =
  776. $NumElements * $WordSize; #<------ Fehler Datasize sollte in Byte sein
  777. $IsoSize = &Size_WR + $DataSize;
  778. # Setup the telegram
  779. my @myPDU =
  780. unpack( "C" x &Size_WR, substr( $self->{S7_RW}, 0, &Size_WR ) );
  781. # Whole telegram Size
  782. # PDU Length
  783. $myPDU[2] = ( $IsoSize >> 8 ) % 256;
  784. $myPDU[3] = $IsoSize % 256;
  785. #set PDU Ref
  786. my $myPDUID = $self->GetNextWord();
  787. $myPDU[11] = $myPDUID % 256;
  788. $myPDU[12] = ( $myPDUID >> 8 ) % 256;
  789. # Data Length
  790. $myLength = $DataSize + 4;
  791. $myPDU[15] = ( $myLength >> 8 ) % 256;
  792. $myPDU[16] = $myLength % 256;
  793. # Function
  794. $myPDU[17] = 0x05;
  795. $myPDU[20] = 0x0a; # Length of remaining bytes
  796. $myPDU[21] = 0x10; # syntag ID
  797. # Set DB Number
  798. $myPDU[27] = $Area;
  799. if ( $Area == &S7ClientBase::S7AreaDB ) {
  800. $myPDU[25] = ( $DBNumber >> 8 ) % 256;
  801. $myPDU[26] = $DBNumber % 256;
  802. }
  803. # Adjusts Start
  804. if ( ( $WordLen == &S7WLBit )
  805. || ( $WordLen == &S7WLCounter )
  806. || ( $WordLen == &S7WLTimer ) )
  807. {
  808. $Address = $Start;
  809. }
  810. else {
  811. $Address = $Start << 3;
  812. }
  813. # Address into the PLC
  814. $myPDU[30] = $Address % 256;
  815. $Address = $Address >> 8;
  816. $myPDU[29] = $Address % 256;
  817. $Address = $Address >> 8;
  818. $myPDU[28] = $Address % 256;
  819. #transport size
  820. my $bytesProElement;
  821. if ( $WordLen == &S7WLBit ) {
  822. $myPDU[32] = &TS_ResBit;
  823. $bytesProElement = 1;
  824. }
  825. # elsif ($WordLen == &S7WLWord) { #V2.8 will be send as Bytes!
  826. # $myPDU[32] = &TS_ResInt;
  827. # $bytesProElement = 2;
  828. # }
  829. # elsif ($WordLen == &S7WLDWord) {
  830. # $myPDU[32] = &TS_ResInt;
  831. # $bytesProElement = 4;
  832. # }
  833. elsif ( $WordLen == &S7WLInt ) {
  834. $myPDU[32] = &TS_ResInt;
  835. $bytesProElement = 2;
  836. }
  837. elsif ( $WordLen == &S7WLDInt ) {
  838. $myPDU[32] = &TS_ResInt;
  839. $bytesProElement = 4;
  840. }
  841. elsif ( $WordLen == &S7WLReal ) {
  842. $myPDU[32] = &TS_ResReal;
  843. $bytesProElement = 4;
  844. }
  845. elsif ( $WordLen == &S7WLChar ) {
  846. $myPDU[32] = &TS_ResOctet;
  847. $bytesProElement = 1;
  848. }
  849. elsif ( $WordLen == &S7WLCounter ) {
  850. $myPDU[32] = &TS_ResOctet;
  851. $bytesProElement = 2;
  852. }
  853. elsif ( $WordLen == &S7WLTimer ) {
  854. $myPDU[32] = &TS_ResOctet;
  855. $bytesProElement = 2;
  856. }
  857. else {
  858. $myPDU[32] = &TS_ResByte;
  859. $bytesProElement = 1;
  860. }
  861. if ( ( $myPDU[32] != &TS_ResOctet )
  862. && ( $myPDU[32] != &TS_ResReal )
  863. && ( $myPDU[32] != &TS_ResBit ) )
  864. {
  865. $myLength = $DataSize << 3;
  866. }
  867. else {
  868. $myLength = $DataSize;
  869. }
  870. # Num elements
  871. my $nElements = int( $NumElements / $bytesProElement );
  872. $myPDU[23] = ( $nElements >> 8 ) % 256;
  873. $myPDU[24] = ($nElements) % 256;
  874. #set word length
  875. $myPDU[22] = $WordLen;
  876. # Length
  877. $myPDU[33] = ( $myLength >> 8 ) % 256;
  878. $myPDU[34] = $myLength % 256;
  879. $self->{PDU}->{H} = pack( "C" x &Size_WR, @myPDU );
  880. # Copy data
  881. $self->{PDU}->{DATA} = substr( $ptrData, $Offset, $DataSize );
  882. if ( $main::attr{global}{verbose} <= 5 ) {
  883. my $b = join(
  884. ", ",
  885. unpack(
  886. "H2 " x $IsoSize,
  887. $self->{PDU}->{H} . $self->{PDU}->{DATA}
  888. )
  889. );
  890. main::Log3 (undef, 5,
  891. "TCPClient WriteArea (IP= " . $self->{Peer} . "): $b");
  892. }
  893. if (
  894. $self->{TCPClient}->send( $self->{PDU}->{H} . $self->{PDU}->{DATA} )
  895. == $IsoSize )
  896. {
  897. # if (send($self->{TCPClient}, $self->{PDU}->{H}.$self->{PDU}->{DATA}, &MSG_NOSIGNAL)== $IsoSize)
  898. ( $res, $myLength ) = $self->RecvISOPacket();
  899. if ( $self->{LastError} == 0 ) {
  900. if ( $myLength == 15 ) {
  901. @myPDU = unpack( "C" x &Size_WR, $self->{PDU}->{H} );
  902. if ( ( $myPDU[27] != 0x00 )
  903. || ( $myPDU[28] != 0x00 )
  904. || ( $myPDU[31] != 0xFF ) )
  905. {
  906. $self->{LastError} = &errS7DataWrite;
  907. #CPU has sent an Error?
  908. my $cpuErrorCode = $myPDU[31];
  909. my $error = $self->getCPUErrorStr($cpuErrorCode);
  910. my $msg =
  911. "TCPClient WriteArea error: $cpuErrorCode = $error";
  912. main::Log3 (undef, 3, $msg);
  913. }
  914. }
  915. else {
  916. $self->{LastError} = &errS7InvalidPDU;
  917. }
  918. }
  919. }
  920. else {
  921. $self->{LastError} = &errTCPDataSend;
  922. }
  923. $Offset += $DataSize;
  924. $TotElements -= $NumElements;
  925. $Start += $NumElements * $WordSize;
  926. }
  927. return $self->{LastError};
  928. }
  929. #-----------------------------------------------------------------------------
  930. sub getPLCDateTime() {
  931. my ($self) = @_;
  932. my $IsoSize;
  933. my $res;
  934. my $TotElements;
  935. main::Log3 (undef, 3, "TCPClient getPLCDateTime:");
  936. # Setup the telegram
  937. my @myPDU = unpack( "C" x &Size_DT, substr( $self->{S7_RW}, 0, &Size_DT ) );
  938. # Whole telegram Size
  939. # PDU Length
  940. $IsoSize = &Size_DT;
  941. $myPDU[2] = ( $IsoSize >> 8 ) % 256;
  942. $myPDU[3] = $IsoSize % 256;
  943. $myPDU[8] = 0x07; #job type = userdata
  944. $myPDU[9] = 0x00; # Redundancy identification
  945. $myPDU[10] = 0x00;
  946. #set PDU Ref
  947. my $myPDUID = $self->GetNextWord();
  948. $myPDU[11] = ( $myPDUID >> 8 ) % 256;
  949. $myPDU[12] = $myPDUID % 256;
  950. #parameter length
  951. $myPDU[13] = 0x00;
  952. $myPDU[14] = 0x08;
  953. # Data Length
  954. my $myLength = 4;
  955. $myPDU[15] = ( $myLength >> 8 ) % 256;
  956. $myPDU[16] = $myLength % 256;
  957. # Function
  958. $myPDU[17] = 0x04; #read
  959. #set parameter heads
  960. $myPDU[18] = 0x01; # Items count
  961. $myPDU[19] = 0x12; # Var spec.
  962. $myPDU[20] = 0x04; # Length of remaining bytes
  963. $myPDU[21] = 0x11; # uk
  964. $myPDU[22] = 0x47; # tg = grClock
  965. $myPDU[23] = 0x01; #subfunction: Read Clock (Date and Time)
  966. $myPDU[24] = 0x00; #Seq
  967. $self->{PDU}->{H} =
  968. pack( "C" x &Size_DT, @myPDU ) . substr( $self->{PDU}->{H}, &Size_DT );
  969. my $b = join( ", ", unpack( "H2 " x &Size_DT, $self->{PDU}->{H} ) );
  970. main::Log3 (undef, 3,
  971. "TCPClient getPLCDateTime (IP= " . $self->{Peer} . "): $b");
  972. $b = substr( $self->{PDU}->{H}, 0, &Size_DT );
  973. if ( $self->{TCPClient}->send($b) == &Size_DT ) {
  974. # main::Log3 undef, 3,"TCPClient getPLCDateTime request sent";
  975. ( $res, $myLength ) = $self->RecvISOPacket();
  976. main::Log3 (undef, 3, "TCPClient getPLCDateTime RecvISOPacket $res");
  977. if ( $self->{LastError} == 0 ) {
  978. if ( $myLength >= 18 ) {
  979. @myPDU = unpack( "C" x $myLength, $self->{PDU}->{H} );
  980. my $b = join(
  981. ", ",
  982. unpack(
  983. "H2 " x $myLength,
  984. $self->{PDU}->{H} . $self->{PDU}->{DATA}
  985. )
  986. );
  987. main::Log3 (undef, 3,
  988. "TCPClient getPLCDateTime getPLCTime Result (IP= "
  989. . $self->{Peer} . "): $b");
  990. }
  991. else {
  992. $self->{LastError} = &errS7InvalidPDU;
  993. main::Log3 (undef, 3,
  994. "TCPClient getPLCDateTime errS7InvalidPDU length $myLength");
  995. }
  996. }
  997. }
  998. else {
  999. $self->{LastError} = &errTCPDataSend;
  1000. main::Log3 (undef, 3, "TCPClient getPLCDateTime errTCPDataSend");
  1001. }
  1002. return ( $self->{LastError}, 0 );
  1003. }
  1004. #-----------------------------------------------------------------------------
  1005. sub version {
  1006. return "1.1";
  1007. }
  1008. #-----------------------------------------------------------------------------
  1009. sub getErrorStr {
  1010. my ( $self, $errorCode ) = @_;
  1011. if ( $errorCode == &errTCPConnectionFailed ) {
  1012. return "TCP Connection error";
  1013. }
  1014. elsif ( $errorCode == &errTCPConnectionReset ) {
  1015. return "Connection reset by the peer";
  1016. }
  1017. elsif ( $errorCode == &errTCPDataRecvTout ) {
  1018. return "A timeout occurred waiting a reply.";
  1019. }
  1020. elsif ( $errorCode == &errTCPDataSend ) {
  1021. return "Ethernet driver returned an error sending the data";
  1022. }
  1023. elsif ( $errorCode == &errTCPDataRecv ) {
  1024. return "Ethernet driver returned an error receiving the data.";
  1025. }
  1026. elsif ( $errorCode == &errISOConnectionFailed ) {
  1027. return "ISO connection failed.";
  1028. }
  1029. elsif ( $errorCode == &errISONegotiatingPDU ) {
  1030. return "ISO PDU negotiation failed";
  1031. }
  1032. elsif ( $errorCode == &errISOInvalidPDU ) {
  1033. return "Malformed PDU supplied.";
  1034. }
  1035. elsif ( $errorCode == &errS7InvalidPDU ) { return "Invalid PDU received."; }
  1036. elsif ( $errorCode == &errS7SendingPDU ) { return "Error sending a PDU."; }
  1037. elsif ( $errorCode == &errS7DataRead ) { return "Error during data read"; }
  1038. elsif ( $errorCode == &errS7DataWrite ) {
  1039. return "Error during data write";
  1040. }
  1041. elsif ( $errorCode == &errS7Function ) {
  1042. return "The PLC reported an error for this function.";
  1043. }
  1044. elsif ( $errorCode == &errBufferTooSmall ) {
  1045. return "The buffer supplied is too small.";
  1046. }
  1047. else { return "unknown errorcode"; }
  1048. }
  1049. sub getCPUErrorStr {
  1050. my ( $self, $errorCode ) = @_;
  1051. if ( $errorCode == &Code7Ok ) { return "CPU: OK"; }
  1052. elsif ( $errorCode == &Code7AddressOutOfRange ) {
  1053. return "CPU: AddressOutOfRange";
  1054. }
  1055. elsif ( $errorCode == &Code7InvalidTransportSize ) {
  1056. return "CPU: Invalid Transport Size";
  1057. }
  1058. elsif ( $errorCode == &Code7WriteDataSizeMismatch ) {
  1059. return "CPU: Write Data Size Mismatch";
  1060. }
  1061. elsif ( $errorCode == &Code7ResItemNotAvailable ) {
  1062. return "CPU: ResItem Not Available";
  1063. }
  1064. elsif ( $errorCode == &Code7ResItemNotAvailable1 ) {
  1065. return "CPU: ResItem Not Available1";
  1066. }
  1067. elsif ( $errorCode == &Code7InvalidValue ) { return "CPU: Invalid Value"; }
  1068. elsif ( $errorCode == &Code7NeedPassword ) { return "CPU: Need Password"; }
  1069. elsif ( $errorCode == &Code7InvalidPassword ) {
  1070. return "CPU: Invalid Password";
  1071. }
  1072. elsif ( $errorCode == &Code7NoPasswordToClear ) {
  1073. return "CPU: No Password To Clear";
  1074. }
  1075. elsif ( $errorCode == &Code7NoPasswordToSet ) {
  1076. return "CPU: No Password To Set";
  1077. }
  1078. elsif ( $errorCode == &Code7FunNotAvailable ) {
  1079. return "CPU: Fun Not Available";
  1080. }
  1081. elsif ( $errorCode == &Code7DataOverPDU ) { return "CPU: DataOverPDU"; }
  1082. else { return "unknown errorcode"; }
  1083. }
  1084. 1;
  1085. =pod
  1086. =item summary low level interface to S7
  1087. =item summary_DE low level interface to S7
  1088. =begin html
  1089. <p><a name="S7_S7Client"></a></p>
  1090. <h3>S7_S7Client</h3>
  1091. <ul>
  1092. <ul>low level interface to S7</ul>
  1093. </ul>
  1094. =end html
  1095. =begin html_DE
  1096. <p><a name="S7_S7Client"></a></p>
  1097. <h3>S7_S7Client</h3>
  1098. <ul>
  1099. <ul>low level interface to S7</ul>
  1100. </ul>
  1101. =end html_DE
  1102. =cut