44_S7.pm 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335
  1. # $Id: 44_S7.pm 14697 2017-07-13 04:36:34Z charlie71 $
  2. ####################################################
  3. package main;
  4. use strict;
  5. use warnings;
  6. #use Devel::NYTProf; #profiler
  7. require "44_S7_S7Client.pm";
  8. require "44_S7_S5Client.pm";
  9. my %gets = (
  10. "S7TCPClientVersion" => "",
  11. "PLCTime" => ""
  12. );
  13. my %sets = (
  14. "intervall" => ""
  15. );
  16. my @areasconfig = (
  17. "ReadInputs-Config", "ReadOutputs-Config",
  18. "ReadFlags-Config", "ReadDB-Config",
  19. "WriteInputs-Config", "WriteOutputs-Config",
  20. "WriteFlags-Config", "WriteDB-Config"
  21. );
  22. my @s7areas = (
  23. &S7ClientBase::S7AreaPE, &S7ClientBase::S7AreaPA, &S7ClientBase::S7AreaMK,
  24. &S7ClientBase::S7AreaDB, &S7ClientBase::S7AreaPE, &S7ClientBase::S7AreaPA,
  25. &S7ClientBase::S7AreaMK, &S7ClientBase::S7AreaDB
  26. );
  27. my @areaname =
  28. ( "inputs", "outputs", "flags", "db", "inputs", "outputs", "flags", "db" );
  29. #####################################
  30. sub S7_Initialize($) { #S5_OK
  31. my $hash = shift @_;
  32. # Provider
  33. $hash->{Clients} = ":S7_DRead:S7_ARead:S7_AWrite:S7_DWrite:";
  34. my %matchList = (
  35. "1:S7_DRead" => "^DR",
  36. "2:S7_DWrite" => "^DW",
  37. "3:S7_ARead" => "^AR",
  38. "4:S7_AWrite" => "^AW"
  39. );
  40. $hash->{MatchList} = \%matchList;
  41. # Consumer
  42. $hash->{DefFn} = "S7_Define";
  43. $hash->{UndefFn} = "S7_Undef";
  44. $hash->{GetFn} = "S7_Get";
  45. $hash->{SetFn} = "S7_Set";
  46. $hash->{AttrFn} = "S7_Attr";
  47. $hash->{AttrList} = "MaxMessageLength Intervall receiveTimeoutMs " . $readingFnAttributes;
  48. # $hash->{AttrList} = join( " ", @areasconfig )." PLCTime";
  49. }
  50. #####################################
  51. sub S7_connect($) {
  52. my $hash = shift @_;
  53. my $name = $hash->{NAME};
  54. if ( $hash->{STATE} eq "connected to PLC" ) {
  55. Log3( $name, 2, "$name S7_connect: allready connected!" );
  56. return;
  57. }
  58. Log3( $name, 4,
  59. "S7: $name connect PLC_address="
  60. . $hash->{plcAddress}
  61. . ", LocalTSAP="
  62. . $hash->{LocalTSAP}
  63. . ", RemoteTSAP="
  64. . $hash->{RemoteTSAP}
  65. . " " );
  66. if ( !defined( $hash->{S7PLCClient} ) ) {
  67. S7_reconnect($hash);
  68. return;
  69. }
  70. $hash->{STATE} = "disconnected";
  71. main::readingsSingleUpdate( $hash, "state", "disconnected", 1 );
  72. my $res;
  73. if ( $hash->{S7TYPE} eq "S5" ) {
  74. eval {
  75. local $SIG{__DIE__} = sub {
  76. my ($s) = @_;
  77. Log3( $hash, 0, "S7_connect: $s" );
  78. $res = -1;
  79. };
  80. $res =
  81. $hash->{S7PLCClient}->S5ConnectPLCAS511( $hash->{plcAddress} );
  82. };
  83. }
  84. else {
  85. $hash->{S7PLCClient}
  86. ->SetConnectionParams( $hash->{plcAddress}, $hash->{LocalTSAP},
  87. $hash->{RemoteTSAP} );
  88. eval {
  89. local $SIG{__DIE__} = sub {
  90. my ($s) = @_;
  91. Log3( $hash, 0, "S7_connect: $s" );
  92. $res = -1;
  93. };
  94. $res = $hash->{S7PLCClient}->Connect();
  95. };
  96. }
  97. if ($res) {
  98. Log3( $name, 2, "S7_connect: $name Could not connect to PLC ($res)" );
  99. return;
  100. }
  101. my $PDUlength = $hash->{S7PLCClient}->{PDULength};
  102. $hash->{maxPDUlength} = $PDUlength;
  103. Log3( $name, 3,
  104. "$name S7_connect: connect to PLC with maxPDUlength=$PDUlength" );
  105. $hash->{STATE} = "connected to PLC";
  106. main::readingsSingleUpdate( $hash, "state", "connected to PLC", 1 );
  107. return undef;
  108. }
  109. #####################################
  110. sub S7_disconnect($) { #S5 OK
  111. my $hash = shift @_;
  112. my ( $ph, $res, $di );
  113. my $name = $hash->{NAME};
  114. my $error = "";
  115. $hash->{S7PLCClient}->Disconnect() if ( defined( $hash->{S7PLCClient} ) );
  116. $hash->{S7PLCClient} = undef; #PLC Client freigeben
  117. $hash->{STATE} = "disconnected";
  118. main::readingsSingleUpdate( $hash, "state", "disconnected", 1 );
  119. Log3( $name, 2, "$name S7 disconnected" );
  120. }
  121. #####################################
  122. sub S7_reconnect($) { #S5 OK
  123. my $hash = shift @_;
  124. S7_disconnect($hash) if ( defined( $hash->{S7PLCClient} ) );
  125. if ( $hash->{S7TYPE} eq "S5" ) {
  126. $hash->{S7PLCClient} = S5Client->new();
  127. }
  128. else {
  129. $hash->{S7PLCClient} = S7Client->new();
  130. }
  131. InternalTimer( gettimeofday() + 3, "S7_connect", $hash, 1 )
  132. ; #wait 3 seconds for reconnect
  133. }
  134. #####################################
  135. sub S7_Define($$) { # S5 OK
  136. my ( $hash, $def ) = @_;
  137. my @a = split( "[ \t][ \t]*", $def );
  138. my ( $name, $PLC_address, $LocalTSAP, $RemoteTSAP, $res, $PDUlength, $rack,
  139. $slot );
  140. $name = $a[0];
  141. if ( uc $a[2] eq "S5" ) {
  142. $hash->{S7TYPE} = "S5";
  143. $PLC_address = $a[3];
  144. if (@a > 4) {
  145. $hash->{Interval} = $a[4];
  146. } else {
  147. $hash->{Interval} = 1;
  148. }
  149. $LocalTSAP = -1;
  150. $RemoteTSAP = -1;
  151. $PDUlength = 240;
  152. }
  153. elsif ( uc $a[2] eq "LOGO7" || uc $a[2] eq "LOGO8" ) {
  154. $PLC_address = $a[3];
  155. $LocalTSAP = 0x0100;
  156. $RemoteTSAP = 0x0200;
  157. if (@a > 4) {
  158. $hash->{Interval} = $a[4];
  159. } else {
  160. $hash->{Interval} = 1;
  161. }
  162. if ( uc $a[2] eq "LOGO7" ) {
  163. $hash->{S7TYPE} = "LOGO7";
  164. }
  165. else {
  166. $hash->{S7TYPE} = "LOGO8";
  167. }
  168. $PDUlength = 240;
  169. }
  170. else {
  171. $PLC_address = $a[2];
  172. $rack = int( $a[3] );
  173. return "invalid rack parameter (0 - 15)"
  174. if ( $rack < 0 || $rack > 15 );
  175. $slot = int( $a[4] );
  176. return "invalid slot parameter (0 - 15)"
  177. if ( $slot < 0 || $slot > 15 );
  178. $hash->{Interval} = 1;
  179. if ( int(@a) == 6 ) {
  180. $hash->{Interval} = int( $a[5] );
  181. return "invalid intervall parameter (1 - 86400)"
  182. if ( $hash->{Interval} < 1 || $hash->{Interval} > 86400 );
  183. }
  184. $LocalTSAP = 0x0100;
  185. $RemoteTSAP = ( &S7Client::S7_PG << 8 ) + ( $rack * 0x20 ) + $slot;
  186. $PDUlength = 0x3c0;
  187. $hash->{S7TYPE} = "NATIVE";
  188. }
  189. $hash->{plcAddress} = $PLC_address;
  190. $hash->{LocalTSAP} = $LocalTSAP;
  191. $hash->{RemoteTSAP} = $RemoteTSAP;
  192. $hash->{maxPDUlength} = $PDUlength; #initial PDU length
  193. $hash->{receiveTimeoutMs} = 500; #default receiving timeout = 500ms
  194. Log3 $name, 4,
  195. "S7: define $name PLC_address=$PLC_address,LocalTSAP=$LocalTSAP, RemoteTSAP=$RemoteTSAP ";
  196. $hash->{STATE} = "disconnected";
  197. main::readingsSingleUpdate( $hash, "state", "disconnected", 1 );
  198. S7_connect($hash);
  199. InternalTimer( gettimeofday() + $hash->{Interval},
  200. "S7_GetUpdate", $hash, 0 );
  201. return undef;
  202. }
  203. #####################################
  204. sub S7_Undef($) { #S5 OK
  205. my $hash = shift;
  206. RemoveInternalTimer($hash);
  207. S7_disconnect($hash);
  208. delete( $modules{S7}{defptr} );
  209. return undef;
  210. }
  211. #####################################
  212. sub S7_Set($@) {
  213. }
  214. #####################################
  215. sub S7_Get($@) { #S5 OK
  216. my ( $hash, @a ) = @_;
  217. return "Need at least one parameters" if ( @a < 2 );
  218. return "Unknown argument $a[1], choose one of "
  219. . join( " ", sort keys %gets )
  220. if ( !defined( $gets{ $a[1] } ) );
  221. my $name = shift @a;
  222. my $cmd = shift @a;
  223. ARGUMENT_HANDLER: {
  224. $cmd eq "S7TCPClientVersion" and do {
  225. return $hash->{S7PLCClient}->version();
  226. last;
  227. };
  228. $cmd eq "PLCTime" and do {
  229. return $hash->{S7PLCClient}->getPLCDateTime();
  230. last;
  231. };
  232. }
  233. }
  234. #####################################
  235. sub S7_Attr(@) {
  236. my ( $cmd, $name, $aName, $aVal ) = @_;
  237. my $hash = $defs{$name};
  238. # $cmd can be "del" or "set"
  239. # $name is device name
  240. # aName and aVal are Attribute name and value
  241. if ( $cmd eq "set" ) {
  242. if ( $aName eq "MaxMessageLength" ) {
  243. if ( $aVal < $hash->{S7PLCClient}->{MaxReadLength} ) {
  244. $hash->{S7PLCClient}->{MaxReadLength} = $aVal;
  245. Log3( $name, 3, "$name S7_Attr: setting MaxReadLength= $aVal" );
  246. }
  247. } elsif ($aName eq "Intervall") {
  248. if ( $aVal >= 1 ) {
  249. $hash->{Interval} = $aVal;
  250. Log3( $name, 3, "$name S7_Attr: setting Intervall= $aVal" );
  251. }
  252. } elsif ($aName eq "receiveTimeoutMs") {
  253. if ( $aVal > 100 && $aVal < 10000) {
  254. $hash->{receiveTimeoutMs} = $aVal;
  255. Log3( $name, 3, "$name S7_Attr: setting receiveTimeoutMs= $aVal" );
  256. #reconnect with the new receiving timeout
  257. $hash->{S7PLCClient}->setRecvTimeout($hash->{receiveTimeoutMs}) if ( defined( $hash->{S7PLCClient} ) );
  258. }
  259. }
  260. ###########
  261. if ( $aName eq "WriteInputs-Config"
  262. || $aName eq "WriteOutputs-Config"
  263. || $aName eq "WriteFlags-Config"
  264. || $aName eq "WriteDB-Config" )
  265. {
  266. my $PDUlength = $hash->{maxPDUlength};
  267. my @a = split( "[ \t][ \t]*", $aVal );
  268. if ( int(@a) % 3 != 0 || int(@a) == 0 ) {
  269. Log3( $name, 3,
  270. "S7: Invalid $aName in attr $name $aName $aVal: $@" );
  271. return
  272. "Invalid $aName $aVal \n Format: <DB> <STARTPOSITION> <LENGTH> [<DB> <STARTPOSITION> <LENGTH> ]";
  273. }
  274. else {
  275. for ( my $i = 0 ; $i < int(@a) ; $i++ ) {
  276. if ( $a[$i] ne int( $a[$i] ) ) {
  277. my $s = $a[$i];
  278. Log3( $name, 3,
  279. "S7: Invalid $aName in attr $name $aName $aVal ($s is not a number): $@"
  280. );
  281. return "Invalid $aName $aVal: $s is not a number";
  282. }
  283. if ( $i % 3 == 0 && ( $a[$i] < 0 || $a[$i] > 1024 ) ) {
  284. Log3( $name, 3,
  285. "S7: Invalid $aName db. valid db 0 - 1024: $@" );
  286. return
  287. "Invalid $aName length: $aVal db: valid db 0 - 1024";
  288. }
  289. if ( $i % 3 == 1 && ( $a[$i] < 0 || $a[$i] > 32768 ) ) {
  290. Log3( $name, 3,
  291. "S7: Invalid $aName startposition. valid startposition 0 - 32768: $@"
  292. );
  293. return
  294. "Invalid $aName startposition: $aVal db: valid startposition 0 - 32768";
  295. }
  296. if ( $i % 3 == 2
  297. && ( $a[$i] < 1 || $a[$i] > $PDUlength ) )
  298. {
  299. Log3( $name, 3,
  300. "S7: Invalid $aName length. valid length 1 - $PDUlength: $@"
  301. );
  302. return
  303. "Invalid $aName lenght: $aVal: valid length 1 - $PDUlength";
  304. }
  305. }
  306. return undef if ( $hash->{STATE} ne "connected to PLC" );
  307. #we need to fill-up the internal buffer from current PLC values
  308. my $hash = $defs{$name};
  309. my $res =
  310. S7_getAllWritingBuffersFromPLC( $hash, $aName, $aVal );
  311. if ( int($res) != 0 ) {
  312. #quit because of error
  313. return $res;
  314. }
  315. }
  316. }
  317. }
  318. return undef;
  319. }
  320. #####################################
  321. sub S7_getAreaIndex4AreaName($) { #S5 OK
  322. my ($aName) = @_;
  323. my $AreaIndex = -1;
  324. for ( my $j = 0 ; $j < int(@areaname) ; $j++ ) {
  325. if ( $aName eq $areasconfig[$j] || $aName eq $areaname[$j] ) {
  326. $AreaIndex = $j;
  327. last;
  328. }
  329. }
  330. if ( $AreaIndex < 0 ) {
  331. Log3( undef, 2, "S7_Attr: Internal error invalid WriteAreaIndex" );
  332. return "Internal error invalid WriteAreaIndex";
  333. }
  334. return $AreaIndex;
  335. }
  336. #####################################
  337. sub S7_WriteToPLC($$$$$$) {
  338. my ( $hash, $areaIndex, $dbNr, $startByte, $WordLen, $dataBlock ) = @_;
  339. my $PDUlength = -1;
  340. if ( defined $hash->{maxPDUlength} ) {
  341. $PDUlength = $hash->{maxPDUlength};
  342. }
  343. my $name = $hash->{NAME};
  344. my $res = -1;
  345. my $Bufferlength = 59999;
  346. $Bufferlength = length($dataBlock);
  347. if ( $Bufferlength <= $PDUlength ) {
  348. if ( $hash->{STATE} eq "connected to PLC" ) {
  349. my $bss = join( ", ", unpack( "H2" x $Bufferlength, $dataBlock ) );
  350. Log3( $name, 5,
  351. "$name S7_WriteToPLC: Write Bytes to PLC: $areaIndex, $dbNr,$startByte , $Bufferlength, $bss"
  352. );
  353. eval {
  354. local $SIG{__DIE__} = sub {
  355. my ($s) = @_;
  356. print "DIE:$s";
  357. Log3( $hash, 0, "DIE:$s" );
  358. $res = -2;
  359. };
  360. if ( $hash->{S7TYPE} eq "S5" ) {
  361. $res = $hash->{S7PLCClient}->S5WriteS5Bytes(
  362. $s7areas[$areaIndex], $dbNr, $startByte, $Bufferlength,
  363. $dataBlock
  364. );
  365. }
  366. else {
  367. $res =
  368. $hash->{S7PLCClient}
  369. ->WriteArea( $s7areas[$areaIndex], $dbNr, $startByte,
  370. $Bufferlength, $WordLen, $dataBlock );
  371. }
  372. };
  373. if ( $res != 0 ) {
  374. my $error = $hash->{S7PLCClient}->getErrorStr($res);
  375. my $msg = "$name S7_WriteToPLC WriteArea error: $res=$error";
  376. Log3( $name, 3, $msg );
  377. S7_reconnect($hash); #lets try a reconnect
  378. return ( -2, $msg );
  379. }
  380. }
  381. else {
  382. my $msg = "$name S7_WriteToPLC: PLC is not connected ";
  383. Log3( $name, 3, $msg );
  384. S7_reconnect($hash); #lets try a reconnect
  385. return ( -2, $msg );
  386. }
  387. }
  388. else {
  389. my $msg =
  390. "S7_WriteToPLC: wrong block length $Bufferlength (max length $PDUlength)";
  391. Log3( $name, 3, $msg );
  392. return ( -1, $msg );
  393. }
  394. }
  395. #####################################
  396. sub S7_WriteBitToPLC($$$$$) {
  397. my ( $hash, $areaIndex, $dbNr, $bitPosition, $bitValue ) = @_;
  398. my $PDUlength = -1;
  399. if ( defined $hash->{maxPDUlength} ) {
  400. $PDUlength = $hash->{maxPDUlength};
  401. }
  402. my $name = $hash->{NAME};
  403. my $res = -1;
  404. my $Bufferlength = 1;
  405. if ( $Bufferlength <= $PDUlength ) {
  406. if ( $hash->{STATE} eq "connected to PLC" ) {
  407. my $bss = join( ", ", unpack( "H2" x $Bufferlength, $bitValue ) );
  408. Log3( $name, 5,
  409. "$name S7_WriteBitToPLC: Write Bytes to PLC: $areaIndex, $dbNr, $bitPosition , $Bufferlength, $bitValue"
  410. );
  411. eval {
  412. local $SIG{__DIE__} = sub {
  413. my ($s) = @_;
  414. print "DIE:$s";
  415. Log3 $hash, 0, "DIE:$s";
  416. $res = -2;
  417. };
  418. if ( $hash->{S7TYPE} eq "S5" ) {
  419. #todo fix S5 Handling
  420. }
  421. else {
  422. $res =
  423. $hash->{S7PLCClient}
  424. ->WriteArea( $s7areas[$areaIndex], $dbNr, $bitPosition,
  425. $Bufferlength, &S7Client::S7WLBit, chr($bitValue) );
  426. }
  427. };
  428. if ( $res != 0 ) {
  429. my $error = $hash->{S7PLCClient}->getErrorStr($res);
  430. my $msg = "$name S7_WriteBitToPLC WriteArea error: $res=$error";
  431. Log3 $name, 3, $msg;
  432. S7_reconnect($hash); #lets try a reconnect
  433. return ( -2, $msg );
  434. }
  435. }
  436. else {
  437. my $msg = "$name S7_WriteBitToPLC: PLC is not connected ";
  438. Log3 $name, 3, $msg;
  439. return ( -1, $msg );
  440. }
  441. }
  442. else {
  443. my $msg =
  444. "S7_WriteBitToPLC: wrong block length $Bufferlength (max length $PDUlength)";
  445. Log3 $name, 3, $msg;
  446. return ( -1, $msg );
  447. }
  448. }
  449. #####################################
  450. #sub S7_WriteBlockToPLC($$$$$) {
  451. # my ( $hash, $areaIndex, $dbNr, $startByte, $dataBlock ) = @_;
  452. #
  453. #
  454. # return S7_WriteToPLC($hash, $areaIndex, $dbNr, $startByte, &S7Client::S7WLByte, $dataBlock);
  455. #
  456. #}
  457. #####################################
  458. sub S7_ReadBlockFromPLC($$$$$) {
  459. my ( $hash, $areaIndex, $dbNr, $startByte, $requestedLength ) = @_;
  460. my $PDUlength = -1;
  461. if ( defined $hash->{maxPDUlength} ) {
  462. $PDUlength = $hash->{maxPDUlength};
  463. }
  464. my $name = $hash->{NAME};
  465. my $readbuffer = "";
  466. my $res = -1;
  467. if ( $requestedLength <= $PDUlength ) {
  468. if ( $hash->{STATE} eq "connected to PLC" ) {
  469. eval {
  470. local $SIG{__DIE__} = sub {
  471. my ($s) = @_;
  472. print "DIE:$s";
  473. Log3 $hash, 0, "DIE:$s";
  474. $res = -2;
  475. };
  476. if ( $hash->{S7TYPE} eq "S5" ) {
  477. ( $res, $readbuffer ) =
  478. $hash->{S7PLCClient}
  479. ->S5ReadS5Bytes( $s7areas[$areaIndex], $dbNr, $startByte,
  480. $requestedLength );
  481. }
  482. else {
  483. ( $res, $readbuffer ) =
  484. $hash->{S7PLCClient}
  485. ->ReadArea( $s7areas[$areaIndex], $dbNr, $startByte,
  486. $requestedLength, &S7Client::S7WLByte );
  487. }
  488. };
  489. if ( $res != 0 ) {
  490. my $error = $hash->{S7PLCClient}->getErrorStr($res);
  491. my $msg =
  492. "$name S7_ReadBlockFromPLC ReadArea error: $res=$error";
  493. Log3( $name, 3, $msg );
  494. S7_reconnect($hash); #lets try a reconnect
  495. return ( -2, $msg );
  496. }
  497. else {
  498. #reading was OK
  499. return ( 0, $readbuffer );
  500. }
  501. }
  502. else {
  503. my $msg = "$name S7_ReadBlockFromPLC: PLC is not connected ";
  504. Log3( $name, 3, $msg );
  505. return ( -1, $msg );
  506. }
  507. }
  508. else {
  509. my $msg =
  510. "$name S7_ReadBlockFromPLC: wrong block length (max length $PDUlength)";
  511. Log3( $name, 3, $msg );
  512. return ( -1, $msg );
  513. }
  514. }
  515. #####################################
  516. sub S7_setBitInBuffer($$$) { #S5 OK
  517. my ( $bitPosition, $buffer, $newValue ) = @_;
  518. my $Bufferlength = ( length($buffer) + 1 ) / 3;
  519. my $bytePosition = int( $bitPosition / 8 );
  520. # Log3 undef, 3, "S7_setBitInBuffer in: ".length($buffer)." , $Bufferlength , $bytePosition , $bitPosition";
  521. if ( $bytePosition < 0 || $bytePosition > $Bufferlength - 1 ) {
  522. #out off buffer request !!!!!
  523. # Log3 undef, 3, "S7_setBitInBuffer out -1 : ".length($buffer);
  524. return ( -1, undef );
  525. }
  526. my @Writebuffer = unpack( "C" x $Bufferlength,
  527. pack( "H2" x $Bufferlength, split( ",", $buffer ) ) );
  528. my $intrestingBit = $bitPosition % 8;
  529. if ( $newValue eq "on" || $newValue eq "trigger" ) {
  530. $Writebuffer[$bytePosition] |= ( 1 << $intrestingBit );
  531. }
  532. else {
  533. $Writebuffer[$bytePosition] &= ( ( ~( 1 << $intrestingBit ) ) & 0xff );
  534. }
  535. my $resultBuffer = join(
  536. ",",
  537. unpack(
  538. "H2" x $Bufferlength,
  539. pack( "C" x $Bufferlength, @Writebuffer )
  540. )
  541. );
  542. $Bufferlength = length($resultBuffer);
  543. # Log3 undef, 3, "S7_setBitInBuffer out: $Bufferlength";
  544. return ( 0, $resultBuffer );
  545. }
  546. #####################################
  547. sub S7_getBitFromBuffer($$) { #S5 OK
  548. my ( $bitPosition, $buffer ) = @_;
  549. my $Bufferlength = ( length($buffer) * 3 ) - 1;
  550. my $bytePosition = int( $bitPosition / 8 );
  551. if ( $bytePosition < 0 || $bytePosition > length($Bufferlength) ) {
  552. #out off buffer request !!!!!
  553. return "unknown";
  554. }
  555. my @Writebuffer = unpack( "C" x $Bufferlength,
  556. pack( "H2" x $Bufferlength, split( ",", $buffer ) ) );
  557. my $intrestingByte = $Writebuffer[$bytePosition];
  558. my $intrestingBit = $bitPosition % 8;
  559. if ( ( $intrestingByte & ( 1 << $intrestingBit ) ) != 0 ) {
  560. return "on";
  561. }
  562. else {
  563. return "off";
  564. }
  565. }
  566. #####################################
  567. sub S7_getAllWritingBuffersFromPLC($$$) { #S5 OK
  568. #$hash ... from S7 physical modul
  569. #$writerConfig ... writer Config
  570. #$aName ... area name
  571. my ( $hash, $aName, $writerConfig ) = @_;
  572. Log3( $aName, 4, "S7: getAllWritingBuffersFromPLC called" );
  573. my @a = split( "[ \t][ \t]*", $writerConfig );
  574. my $PDUlength = $hash->{maxPDUlength};
  575. my @writingBuffers = ();
  576. my $readbuffer;
  577. my $writeAreaIndex = S7_getAreaIndex4AreaName($aName);
  578. return $writeAreaIndex if ( $writeAreaIndex ne int($writeAreaIndex) );
  579. my $nr = int(@a);
  580. # Log3 undef, 4, "S7: getAllWritingBuffersFromPLC $nr";
  581. my $res;
  582. for ( my $i = 0 ; $i < int(@a) ; $i = $i + 3 ) {
  583. my $readbuffer;
  584. my $res;
  585. my $dbnr = $a[$i];
  586. my $startByte = $a[ $i + 1 ];
  587. my $requestedLength = $a[ $i + 2 ];
  588. ( $res, $readbuffer ) =
  589. S7_ReadBlockFromPLC( $hash, $writeAreaIndex, $dbnr, $startByte,
  590. $requestedLength );
  591. if ( $res == 0 ) { #reading was OK
  592. my $hexbuffer =
  593. join( ",", unpack( "H2" x length($readbuffer), $readbuffer ) );
  594. push( @writingBuffers, $hexbuffer );
  595. }
  596. else {
  597. #error in reading so just return the error MSG
  598. return $readbuffer;
  599. }
  600. }
  601. if ( int(@writingBuffers) > 0 ) {
  602. $hash->{"${areaname[$writeAreaIndex]}_DBWRITEBUFFER"} =
  603. join( " ", @writingBuffers );
  604. }
  605. else {
  606. $hash->{"${areaname[$writeAreaIndex]}_DBWRITEBUFFER"} = undef;
  607. }
  608. return 0;
  609. }
  610. #####################################
  611. sub S7_GetUpdate($) {
  612. my ($hash) = @_;
  613. my $name = $hash->{NAME};
  614. Log3( $name, 4, "S7: $name GetUpdate called ..." );
  615. my $res = S7_readFromPLC($hash);
  616. if ( $res == 0 ) {
  617. InternalTimer( gettimeofday() + $hash->{Interval},
  618. "S7_GetUpdate", $hash, 1 );
  619. }
  620. else {
  621. #an error has occoured --> 10sec break
  622. InternalTimer( gettimeofday() + 10, "S7_GetUpdate", $hash, 1 );
  623. }
  624. }
  625. #####################################
  626. sub S7_dispatchMsg($$$$$$$$) {
  627. my ( $hash, $msgprefix, $areaIndex, $dbNr, $startByte, $hexbuffer, $length,
  628. $clientsNames )
  629. = @_;
  630. my $name = $hash->{NAME};
  631. my $dmsg =
  632. $msgprefix . " "
  633. . $areaname[$areaIndex] . " "
  634. . $dbNr . " "
  635. . $startByte . " "
  636. . $length . " "
  637. . $name . " "
  638. . $hexbuffer . " "
  639. . $clientsNames;
  640. Log3( $name, 5, $name . " S7_dispatchMsg " . $dmsg );
  641. Dispatch( $hash, $dmsg, {} );
  642. }
  643. #####################################
  644. sub S7_readAndDispatchBlockFromPLC($$$$$$$$$$) { #S5 OK
  645. my (
  646. $hash, $area, $dbnr,
  647. $blockstartpos, $blocklength, $hasAnalogReading,
  648. $hasDigitalReading, $hasAnalogWriting, $hasDigitalWriting,
  649. $clientsNames
  650. ) = @_;
  651. my $name = $hash->{NAME};
  652. my $state = $hash->{STATE};
  653. my $areaIndex = S7_getAreaIndex4AreaName($area);
  654. Log3( $name, 4,
  655. $name
  656. . " READ Block AREA="
  657. . $area . " ("
  658. . $areaIndex
  659. . "), DB ="
  660. . $dbnr
  661. . ", ADDRESS="
  662. . $blockstartpos
  663. . ", LENGTH="
  664. . $blocklength );
  665. if ( $state ne "connected to PLC" ) {
  666. Log3 $name, 3, "$name is disconnected ? --> reconnect";
  667. S7_reconnect($hash); #lets try a reconnect
  668. #@nextreadings[ $i / 4 ] = $now + 10; #retry in 10s
  669. return -2;
  670. }
  671. my $res;
  672. my $readbuffer;
  673. ( $res, $readbuffer ) =
  674. S7_ReadBlockFromPLC( $hash, $areaIndex, $dbnr, $blockstartpos,
  675. $blocklength );
  676. if ( $res == 0 ) {
  677. #reading was OK
  678. my $length = length($readbuffer);
  679. my $hexbuffer = join( ",", unpack( "H2" x $length, $readbuffer ) );
  680. #dispatch to reader
  681. S7_dispatchMsg( $hash, "AR", $areaIndex, $dbnr, $blockstartpos,
  682. $hexbuffer, $length, $clientsNames )
  683. if ( $hasAnalogReading > 0 );
  684. S7_dispatchMsg( $hash, "DR", $areaIndex, $dbnr, $blockstartpos,
  685. $hexbuffer, $length, $clientsNames )
  686. if ( $hasDigitalReading > 0 );
  687. #dispatch to writer
  688. S7_dispatchMsg( $hash, "AW", $areaIndex, $dbnr, $blockstartpos,
  689. $hexbuffer, $length, $clientsNames )
  690. if ( $hasAnalogWriting > 0 );
  691. S7_dispatchMsg( $hash, "DW", $areaIndex, $dbnr, $blockstartpos,
  692. $hexbuffer, $length, $clientsNames )
  693. if ( $hasDigitalWriting > 0 );
  694. return 0;
  695. }
  696. else {
  697. #reading failed
  698. return -1;
  699. }
  700. }
  701. #####################################
  702. sub S7_getReadingsList($) { #S5 OK
  703. my ($hash) = @_;
  704. my $name = $hash->{NAME};
  705. my @readings;
  706. # Jetzt suchen wir alle Readings
  707. my @mykeys;
  708. my %logoClients;
  709. @mykeys =
  710. grep $defs{$_}{TYPE} =~ /^S7_/ && $defs{$_}{IODev}{NAME} eq $hash->{NAME},
  711. keys(%defs);
  712. @logoClients{@mykeys} =
  713. @defs{@mykeys}; #jetzt haben wir alle clients in logoClients
  714. #we need to find out the unique areas
  715. my %tmphash = map { $logoClients{$_}{AREA} => 1 } keys %logoClients;
  716. my @uniqueArea = keys %tmphash;
  717. foreach my $Area (@uniqueArea) {
  718. my %logoClientsArea;
  719. @mykeys =
  720. grep $defs{$_}{TYPE} =~ /^S7_/
  721. && $defs{$_}{IODev}{NAME} eq $hash->{NAME}
  722. && $defs{$_}{AREA} eq $Area, keys(%defs);
  723. @logoClientsArea{@mykeys} = @defs{@mykeys};
  724. #now we findout which DBs are used (unique)
  725. %tmphash = map { $logoClientsArea{$_}{DB} => 1 } keys %logoClientsArea;
  726. my @uniqueDB = keys %tmphash;
  727. foreach my $DBNr (@uniqueDB) {
  728. #now we filter all readinfy by DB!
  729. my %logoClientsDB;
  730. @mykeys =
  731. grep $defs{$_}{TYPE} =~ /^S7_/
  732. && $defs{$_}{IODev}{NAME} eq $hash->{NAME}
  733. && $defs{$_}{AREA} eq $Area
  734. && $defs{$_}{DB} == $DBNr, keys(%defs);
  735. @logoClientsDB{@mykeys} = @defs{@mykeys};
  736. #next step is, sorting all clients by ADDRESS
  737. my @positioned = sort {
  738. $logoClientsDB{$a}{ADDRESS} <=> $logoClientsDB{$b}{ADDRESS}
  739. } keys %logoClientsDB;
  740. my $blockstartpos = -1;
  741. my $blocklength = 0;
  742. my $hasAnalogReading = 0;
  743. my $hasDigitalReading = 0;
  744. my $hasAnalogWriting = 0;
  745. my $hasDigitalWriting = 0;
  746. my $clientsName = "";
  747. for ( my $i = 0 ; $i < int(@positioned) ; $i++ ) {
  748. if ( $blockstartpos < 0 ) {
  749. #we start a new block
  750. $blockstartpos =
  751. int( $logoClientsDB{ $positioned[$i] }{ADDRESS} );
  752. $blocklength = $logoClientsDB{ $positioned[$i] }{LENGTH};
  753. $hasAnalogReading++
  754. if (
  755. $logoClientsDB{ $positioned[$i] }{TYPE} eq "S7_ARead" );
  756. $hasDigitalReading++
  757. if (
  758. $logoClientsDB{ $positioned[$i] }{TYPE} eq "S7_DRead" );
  759. $hasAnalogWriting++
  760. if ( $logoClientsDB{ $positioned[$i] }{TYPE} eq
  761. "S7_AWrite" );
  762. $hasDigitalWriting++
  763. if ( $logoClientsDB{ $positioned[$i] }{TYPE} eq
  764. "S7_DWrite" );
  765. $clientsName = $logoClientsDB{ $positioned[$i] }{NAME};
  766. }
  767. else {
  768. if ( $logoClientsDB{ $positioned[$i] }{ADDRESS} +
  769. $logoClientsDB{ $positioned[$i] }{LENGTH} -
  770. $blockstartpos <=
  771. $hash->{S7PLCClient}->{MaxReadLength} )
  772. {
  773. #extend existing block
  774. if (
  775. int( $logoClientsDB{ $positioned[$i] }{ADDRESS} ) +
  776. $logoClientsDB{ $positioned[$i] }{LENGTH} -
  777. $blockstartpos > $blocklength )
  778. {
  779. $blocklength =
  780. int( $logoClientsDB{ $positioned[$i] }{ADDRESS} )
  781. + $logoClientsDB{ $positioned[$i] }{LENGTH} -
  782. $blockstartpos;
  783. $hasAnalogReading++
  784. if ( $logoClientsDB{ $positioned[$i] }{TYPE} eq
  785. "S7_ARead" );
  786. $hasDigitalReading++
  787. if ( $logoClientsDB{ $positioned[$i] }{TYPE} eq
  788. "S7_DRead" );
  789. $hasAnalogWriting++
  790. if ( $logoClientsDB{ $positioned[$i] }{TYPE} eq
  791. "S7_AWrite" );
  792. $hasDigitalWriting++
  793. if ( $logoClientsDB{ $positioned[$i] }{TYPE} eq
  794. "S7_DWrite" );
  795. }
  796. $clientsName .=
  797. "," . $logoClientsDB{ $positioned[$i] }{NAME};
  798. }
  799. else {
  800. #block would exeed MaxReadLength
  801. #read and dispatch block from PLC
  802. #block in liste speichern
  803. push(
  804. @readings,
  805. [
  806. $logoClientsDB{ $positioned[$i] }{AREA},
  807. $logoClientsDB{ $positioned[$i] }{DB},
  808. $blockstartpos,
  809. $blocklength,
  810. $hasAnalogReading,
  811. $hasDigitalReading,
  812. $hasAnalogWriting,
  813. $hasDigitalWriting,
  814. $clientsName
  815. ]
  816. );
  817. $hasAnalogReading = 0;
  818. $hasDigitalReading = 0;
  819. $hasAnalogWriting = 0;
  820. $hasDigitalWriting = 0;
  821. #start new block new time
  822. $blockstartpos =
  823. int( $logoClientsDB{ $positioned[$i] }{ADDRESS} );
  824. $blocklength =
  825. $logoClientsDB{ $positioned[$i] }{LENGTH};
  826. $hasAnalogReading++
  827. if ( $logoClientsDB{ $positioned[$i] }{TYPE} eq
  828. "S7_ARead" );
  829. $hasDigitalReading++
  830. if ( $logoClientsDB{ $positioned[$i] }{TYPE} eq
  831. "S7_DRead" );
  832. $hasAnalogWriting++
  833. if ( $logoClientsDB{ $positioned[$i] }{TYPE} eq
  834. "S7_AWrite" );
  835. $hasDigitalWriting++
  836. if ( $logoClientsDB{ $positioned[$i] }{TYPE} eq
  837. "S7_DWrite" );
  838. $clientsName = $logoClientsDB{ $positioned[$i] }{NAME};
  839. }
  840. }
  841. }
  842. if ( $blockstartpos >= 0 ) {
  843. #read and dispatch block from PLC
  844. push(
  845. @readings,
  846. [
  847. $logoClientsDB{ $positioned[ int(@positioned) - 1 ] }
  848. {AREA},
  849. $logoClientsDB{ $positioned[ int(@positioned) - 1 ] }
  850. {DB},
  851. $blockstartpos,
  852. $blocklength,
  853. $hasAnalogReading,
  854. $hasDigitalReading,
  855. $hasAnalogWriting,
  856. $hasDigitalWriting,
  857. $clientsName
  858. ]
  859. );
  860. }
  861. }
  862. }
  863. @{ $hash->{ReadingList} } = @readings;
  864. return 0;
  865. }
  866. #####################################
  867. sub S7_readFromPLC($) { #S5 OK
  868. my ($hash) = @_;
  869. my $name = $hash->{NAME};
  870. my $res;
  871. if ( ( !defined( $hash->{dirty} ) ) || $hash->{dirty} == 1 ) {
  872. S7_getReadingsList($hash);
  873. $hash->{dirty} = 0;
  874. }
  875. my @readingList = @{ $hash->{ReadingList} };
  876. for ( my $i = 0 ; $i < int(@readingList) ; $i++ ) {
  877. my @readingSet = @{ $readingList[$i] };
  878. $res = S7_readAndDispatchBlockFromPLC(
  879. $hash, $readingSet[0], $readingSet[1], $readingSet[2],
  880. $readingSet[3], $readingSet[4], $readingSet[5], $readingSet[6],
  881. $readingSet[7], $readingSet[8]
  882. );
  883. return $res if ( $res != 0 );
  884. }
  885. return 0;
  886. }
  887. 1;
  888. =pod
  889. =item summary basic interface to a SIEMENS S7 / S5
  890. =item summary_DE Schnittstelle zu einer Siemens S7 / S5
  891. =begin html
  892. <p><a name="S7"></a></p>
  893. <h3>S7</h3>
  894. <ul>
  895. <ul>
  896. <ul>This module connects a SIEMENS PLC (S7,S5,SIEMENS Logo!). The TCP communication (S7, Siemens LOGO!) module is based on settimino (http://settimino.sourceforge.net). The serial Communication is based on a libnodave portation.</ul>
  897. </ul>
  898. </ul>
  899. <p>&nbsp;</p>
  900. <ul>
  901. <ul>You can found a german wiki here: httl://www.fhemwiki.de/wiki/S7</ul>
  902. </ul>
  903. <p><br /><br /></p>
  904. <ul>
  905. <ul>For the communication the following modules have been implemented:
  906. <ul>
  907. <li>S7 &hellip; sets up the communication channel to the PLC</li>
  908. <li>S7_ARead &hellip; Is used for reading integer Values from the PLC</li>
  909. <li>S7_AWrite &hellip; Is used for write integer Values to the PLC</li>
  910. <li>S7_DRead &hellip; Is used for read bits</li>
  911. <li>S7_DWrite &hellip; Is used for writing bits.</li>
  912. </ul>
  913. </ul>
  914. </ul>
  915. <p><br /><br /></p>
  916. <ul>
  917. <ul>Reading work flow:</ul>
  918. </ul>
  919. <p><br /><br /></p>
  920. <ul>
  921. <ul>The S7 module reads periodically the configured DB areas from the PLC and stores the data in an internal buffer. Then all reading client modules are informed. Each client module extracts his data and the corresponding readings are set. Writing work flow:</ul>
  922. </ul>
  923. <p><br /><br /></p>
  924. <ul>
  925. <ul>At the S7 module you need to configure the PLC writing target. Also the S7 module holds a writing buffer. Which contains a master copy of the data needs to send.</ul>
  926. </ul>
  927. <p>&nbsp;</p>
  928. <ul>
  929. <ul>(Note: after configuration of the writing area a copy from the PLC is read and used as initial fill-up of the writing buffer)</ul>
  930. </ul>
  931. <p>&nbsp;</p>
  932. <ul>
  933. <ul>Note: The S7 module will send always the whole data block to the PLC. When data on the clients modules is set then the client module updates the internal writing buffer on the S7 module and triggers the writing to the PLC.</ul>
  934. </ul>
  935. <p><br /><br /><a name="S7define"></a><strong>Define</strong><code>define &lt;name&gt; S7 &lt;PLC_address&gt; &lt;rack&gt; &lt;slot&gt; [&lt;Interval&gt;] </code><br /><br /><code>define logo S7 10.0.0.241 2 0 </code></p>
  936. <ul>
  937. <ul>
  938. <ul>
  939. <ul>
  940. <li>PLC_address &hellip; IP address of the S7 PLC (For S5 see below)</li>
  941. <li>rack &hellip; rack of the PLC</li>
  942. <li>slot &hellip; slot of the PLC</li>
  943. <li>Interval &hellip; Intervall how often the modul should check if a reading is required</li>
  944. </ul>
  945. </ul>
  946. </ul>
  947. </ul>
  948. <p>&nbsp;</p>
  949. <ul>
  950. <ul>
  951. <ul>Note: For Siemens logo you should use a alternative (more simply configuration method):</ul>
  952. </ul>
  953. </ul>
  954. <p>&nbsp;</p>
  955. <ul>
  956. <ul>
  957. <ul><code>define logo S7 LOGO7 10.0.0.241</code></ul>
  958. </ul>
  959. </ul>
  960. <p>&nbsp;</p>
  961. <ul>
  962. <ul>
  963. <ul>Note: For Siemens S5 you must use a alternative (more simply configuration method):</ul>
  964. </ul>
  965. </ul>
  966. <p>&nbsp;</p>
  967. <ul>
  968. <ul>
  969. <ul>define logo S7 S5 /dev/tty1 in this case the PLC_address is the serial port number</ul>
  970. </ul>
  971. </ul>
  972. <p><br /><br /><strong>Attr</strong></p>
  973. <ul>
  974. <ul>The following attributes are supported:</ul>
  975. </ul>
  976. <p>&nbsp;</p>
  977. <ul>
  978. <ul>
  979. <ul>
  980. <li>MaxMessageLength</li>
  981. <li>receiveTimeoutMs</li>
  982. <li>Intervall</li>
  983. </ul>
  984. </ul>
  985. </ul>
  986. <p>&nbsp;</p>
  987. <ul>
  988. <ul>MaxMessageLength ... restricts the packet length if lower than the negioated PDULength. This could be used to increate the processing speed. 2 small packages may be smaler than one large package</ul>
  989. <ul>receiveTimeoutMs ... timeout in ms for TCP receiving packages. Default Value 500ms.&nbsp;</ul>
  990. <ul>Intervall ... polling&nbsp;intervall in s&nbsp;</ul>
  991. </ul>
  992. =end html
  993. =begin html_DE
  994. <p><a name="S7"></a></p>
  995. <h3>S7</h3>
  996. <ul>
  997. <ul>
  998. <ul>This module connects a SIEMENS PLC (S7,S5,SIEMENS Logo!). The TCP communication (S7, Siemens LOGO!) module is based on settimino (http://settimino.sourceforge.net). The serial Communication is based on a libnodave portation.</ul>
  999. </ul>
  1000. </ul>
  1001. <p>&nbsp;</p>
  1002. <ul>
  1003. <ul>You can found a german wiki here: httl://www.fhemwiki.de/wiki/S7</ul>
  1004. </ul>
  1005. <p><br /><br /></p>
  1006. <ul>
  1007. <ul>For the communication the following modules have been implemented:
  1008. <ul>
  1009. <li>S7 &hellip; sets up the communication channel to the PLC</li>
  1010. <li>S7_ARead &hellip; Is used for reading integer Values from the PLC</li>
  1011. <li>S7_AWrite &hellip; Is used for write integer Values to the PLC</li>
  1012. <li>S7_DRead &hellip; Is used for read bits</li>
  1013. <li>S7_DWrite &hellip; Is used for writing bits.</li>
  1014. </ul>
  1015. </ul>
  1016. </ul>
  1017. <p><br /><br /></p>
  1018. <ul>
  1019. <ul>Reading work flow:</ul>
  1020. </ul>
  1021. <p><br /><br /></p>
  1022. <ul>
  1023. <ul>The S7 module reads periodically the configured DB areas from the PLC and stores the data in an internal buffer. Then all reading client modules are informed. Each client module extracts his data and the corresponding readings are set. Writing work flow:</ul>
  1024. </ul>
  1025. <p><br /><br /></p>
  1026. <ul>
  1027. <ul>At the S7 module you need to configure the PLC writing target. Also the S7 module holds a writing buffer. Which contains a master copy of the data needs to send.</ul>
  1028. </ul>
  1029. <p>&nbsp;</p>
  1030. <ul>
  1031. <ul>(Note: after configuration of the writing area a copy from the PLC is read and used as initial fill-up of the writing buffer)</ul>
  1032. </ul>
  1033. <p>&nbsp;</p>
  1034. <ul>
  1035. <ul>Note: The S7 module will send always the whole data block to the PLC. When data on the clients modules is set then the client module updates the internal writing buffer on the S7 module and triggers the writing to the PLC.</ul>
  1036. </ul>
  1037. <p><br /><br /><a name="S7define"></a><strong>Define</strong><code>define &lt;name&gt; S7 &lt;PLC_address&gt; &lt;rack&gt; &lt;slot&gt; [&lt;Interval&gt;] </code><br /><br /><code>define logo S7 10.0.0.241 2 0 </code></p>
  1038. <ul>
  1039. <ul>
  1040. <ul>
  1041. <ul>
  1042. <li>PLC_address &hellip; IP address of the S7 PLC (For S5 see below)</li>
  1043. <li>rack &hellip; rack of the PLC</li>
  1044. <li>slot &hellip; slot of the PLC</li>
  1045. <li>Interval &hellip; Intervall how often the modul should check if a reading is required</li>
  1046. </ul>
  1047. </ul>
  1048. </ul>
  1049. </ul>
  1050. <p>&nbsp;</p>
  1051. <ul>
  1052. <ul>
  1053. <ul>Note: For Siemens logo you should use a alternative (more simply configuration method):</ul>
  1054. </ul>
  1055. </ul>
  1056. <p>&nbsp;</p>
  1057. <ul>
  1058. <ul>
  1059. <ul><code>define logo S7 LOGO7 10.0.0.241</code></ul>
  1060. </ul>
  1061. </ul>
  1062. <p>&nbsp;</p>
  1063. <ul>
  1064. <ul>
  1065. <ul>Note: For Siemens S5 you must use a alternative (more simply configuration method):</ul>
  1066. </ul>
  1067. </ul>
  1068. <p>&nbsp;</p>
  1069. <ul>
  1070. <ul>
  1071. <ul>define logo S7 S5 /dev/tty1 in this case the PLC_address is the serial port number</ul>
  1072. </ul>
  1073. </ul>
  1074. <p><br /><br /><strong>Attr</strong></p>
  1075. <ul>
  1076. <ul>The following attributes are supported:</ul>
  1077. </ul>
  1078. <p>&nbsp;</p>
  1079. <ul>
  1080. <ul>
  1081. <ul>
  1082. <li>MaxMessageLength</li>
  1083. <li>receiveTimeoutMs</li>
  1084. <li>Intervall</li>
  1085. </ul>
  1086. </ul>
  1087. </ul>
  1088. <p>&nbsp;</p>
  1089. <ul>
  1090. <ul>MaxMessageLength ... restricts the packet length if lower than the negioated PDULength. This could be used to increate the processing speed. 2 small packages may be smaler than one large package</ul>
  1091. <ul>receiveTimeoutMs ... timeout in ms for TCP receiving packages. Default Value 500ms.&nbsp;</ul>
  1092. <ul>Intervall ... polling&nbsp;intervall in s&nbsp;</ul>
  1093. </ul>
  1094. =end html_DE
  1095. =cut