44_S7_DWrite.pm 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592
  1. # $Id: 44_S7_DWrite.pm 12776 2016-12-14 18:09:08Z charlie71born $
  2. ##############################################
  3. package main;
  4. use strict;
  5. use warnings;
  6. #use Switch;
  7. my %sets = (
  8. "on" => "",
  9. "off" => "",
  10. "toggle" => ""
  11. );
  12. my %gets = (
  13. "reading" => "",
  14. "STATE" => ""
  15. );
  16. #####################################
  17. sub S7_DWrite_Initialize($) {
  18. my $hash = shift @_;
  19. # Provider
  20. # Consumer
  21. $hash->{Match} = "^DW";
  22. $hash->{DefFn} = "S7_DWrite_Define";
  23. $hash->{UndefFn} = "S7_DWrite_Undef";
  24. $hash->{SetFn} = "S7_DWrite_Set";
  25. $hash->{ParseFn} = "S7_DWrite_Parse";
  26. $hash->{AttrFn} = "S7_DWrite_Attr";
  27. $hash->{AttrList} = "IODev trigger_length " . $readingFnAttributes;
  28. main::LoadModule("S7");
  29. }
  30. #####################################
  31. sub S7_DWrite_Undef($$) {
  32. my ( $hash, $name ) = @_;
  33. RemoveInternalTimer($hash);
  34. Log3 $name, 4,
  35. "S7_DWrite ("
  36. . $hash->{IODev}{NAME}
  37. . "): undef "
  38. . $hash->{NAME}
  39. . " Adress:"
  40. . $hash->{ADDRESS};
  41. delete( $modules{S7_DWrite}{defptr} );
  42. return undef;
  43. }
  44. #####################################
  45. sub S7_DWrite_Define($$) {
  46. my ( $hash, $def ) = @_;
  47. my @a = split( "[ \t][ \t]*", $def );
  48. my ( $name, $area, $DB, $position );
  49. my $byte;
  50. my $bit;
  51. $name = $a[0];
  52. Log3 $name, 5, "S7_DWrite_Define called";
  53. AssignIoPort($hash); # logisches modul an physikalisches binden !!!
  54. my $sname = $hash->{IODev}{NAME};
  55. if ( uc $a[2] =~ m/^[QIMN](\d*)/ ) {
  56. $area = "db";
  57. $DB = 0;
  58. my $startposition;
  59. my $Offset;
  60. if ( uc $a[2] =~ m/^Q(\d*)/ ) {
  61. $startposition = 1;
  62. if ( $hash->{IODev}{S7TYPE} eq "LOGO7" ) {
  63. $Offset = 942;
  64. }
  65. elsif ( $hash->{IODev}{S7TYPE} eq "LOGO8" ) {
  66. $Offset = 1064;
  67. }
  68. else {
  69. my $msg =
  70. "wrong syntax : define <name> S7_DWrite {inputs|outputs|flags|db} <DB> <address> \n Only for Logo7 or Logo8:\n define <name> S7_DWrite {I|Q|M|NI|NQ}1..24";
  71. Log3 undef, 2, $msg;
  72. return $msg;
  73. }
  74. }
  75. elsif ( uc $a[2] =~ m/^I(\d*)/ ) {
  76. $startposition = 1;
  77. if ( $hash->{IODev}{S7TYPE} eq "LOGO7" ) {
  78. $Offset = 923;
  79. }
  80. elsif ( $hash->{IODev}{S7TYPE} eq "LOGO8" ) {
  81. $Offset = 1024;
  82. }
  83. else {
  84. my $msg =
  85. "wrong syntax : define <name> S7_DWrite {inputs|outputs|flags|db} <DB> <address> \n Only for Logo7 or Logo8:\n define <name> S7_DWrite {I|Q|M|NI|NQ}1..24";
  86. Log3 undef, 2, $msg;
  87. return $msg;
  88. }
  89. }
  90. elsif ( uc $a[2] =~ m/^NI(\d*)/ ) {
  91. $startposition = 2;
  92. if ( $hash->{IODev}{S7TYPE} eq "LOGO8" ) {
  93. $Offset = 1246;
  94. }
  95. else {
  96. my $msg =
  97. "wrong syntax : define <name> S7_DWrite {inputs|outputs|flags|db} <DB> <address> \n Only for Logo7 or Logo8:\n define <name> S7_DWrite {I|Q|M|NI|NQ}1..24";
  98. Log3 undef, 2, $msg;
  99. return $msg;
  100. }
  101. }
  102. elsif ( uc $a[2] =~ m/^NQ(\d*)/ ) {
  103. $startposition = 2;
  104. if ( $hash->{IODev}{S7TYPE} eq "LOGO8" ) {
  105. $Offset = 1390;
  106. }
  107. else {
  108. my $msg =
  109. "wrong syntax : define <name> S7_DWrite {inputs|outputs|flags|db} <DB> <address> \n Only for Logo7 or Logo8:\n define <name> S7_DWrite {I|Q|M|NI|NQ}1..24";
  110. Log3 undef, 2, $msg;
  111. return $msg;
  112. }
  113. }
  114. elsif ( uc $a[2] =~ m/^M(\d*)/ ) {
  115. $startposition = 1;
  116. if ( $hash->{IODev}{S7TYPE} eq "LOGO7" ) {
  117. $Offset = 948;
  118. }
  119. elsif ( $hash->{IODev}{S7TYPE} eq "LOGO8" ) {
  120. $Offset = 1104;
  121. }
  122. else {
  123. my $msg =
  124. "wrong syntax : define <name> S7_DWrite {inputs|outputs|flags|db} <DB> <address> \n Only for Logo7 or Logo8:\n define <name> S7_DWrite {I|Q|M|NI|NQ}1..24";
  125. Log3 undef, 2, $msg;
  126. return $msg;
  127. }
  128. }
  129. else {
  130. my $msg =
  131. "wrong syntax : define <name> S7_DWrite {inputs|outputs|flags|db} <DB> <address> \n Only for Logo7 or Logo8:\n define <name> S7_DWrite {I|Q|M|NI|NQ}1..24";
  132. Log3 undef, 2, $msg;
  133. return $msg;
  134. }
  135. $position =
  136. ( $Offset * 8 ) + int( substr( $a[2], $startposition ) ) - 1;
  137. $byte = int( $position / 8 );
  138. $bit = ( $position % 8 );
  139. }
  140. else {
  141. $area = lc $a[2];
  142. $DB = $a[3];
  143. $position = $a[4];
  144. if ( $area ne "inputs"
  145. && $area ne "outputs"
  146. && $area ne "flags"
  147. && $area ne "db" )
  148. {
  149. my $msg =
  150. "wrong syntax: define <name> S7_DWrite {inputs|outputs|flags|db} <DB> <address> \n Only for Logo7 or Logo8:\n define <name> S7_DWrite {I|Q|M}1..24";
  151. Log3 undef, 2, $msg;
  152. return $msg;
  153. }
  154. my @address = split( /\./, $position );
  155. if ( int(@address) == 2 ) {
  156. $byte = $address[0];
  157. $bit = $address[1];
  158. }
  159. else {
  160. $byte = int( $address[0] / 8 );
  161. $bit = ( $address[0] % 8 );
  162. }
  163. $position = ( $byte * 8 ) + $bit;
  164. }
  165. Log3 $name, 5, "S7_DWrite_Define called2";
  166. $hash->{ADDRESS} = "$byte.$bit";
  167. $hash->{AREA} = $area;
  168. $hash->{DB} = $DB;
  169. $hash->{LENGTH} = 1;
  170. $hash->{POSITION} = $position;
  171. my $ID = "$area $DB";
  172. if ( !defined( $modules{S7_DWrite}{defptr}{$ID} ) ) {
  173. my @b = ();
  174. push( @b, $hash );
  175. $modules{S7_DWrite}{defptr}{$ID} = \@b;
  176. }
  177. else {
  178. push( @{ $modules{S7_DWrite}{defptr}{$ID} }, $hash );
  179. }
  180. $hash->{IODev}{dirty} = 1;
  181. return undef;
  182. }
  183. #####################################
  184. sub S7_DWrite_setABit($$) {
  185. my ( $hash, $newValue ) = @_;
  186. my $name = $hash->{NAME};
  187. $newValue = lc $newValue;
  188. Log3 $name, 4, "S7_DWrite_setABit $newValue";
  189. if ( $newValue ne "on" && $newValue ne "off" && $newValue ne "trigger" ) {
  190. return "Unknown argument $newValue, choose one of ON OFF TRIGGER";
  191. }
  192. my $sname = $hash->{IODev}{NAME};
  193. my $position = $hash->{POSITION};
  194. my $area = $hash->{AREA};
  195. my $dbNR = $hash->{DB};
  196. my $shash = $defs{$sname};
  197. my $writeAreaIndex = S7_getAreaIndex4AreaName($area);
  198. return $writeAreaIndex if ( $writeAreaIndex ne int($writeAreaIndex) );
  199. my $b = 0;
  200. my $res;
  201. if ( $newValue eq "on" || $newValue eq "trigger" ) {
  202. $b = 1;
  203. }
  204. if ( $shash->{S7TYPE} eq "S5" ) {
  205. #S5
  206. #lesen wir das aktuelle byte
  207. my $byte = int( $position / 8 );
  208. my $bit = int( $position % 8 );
  209. my $readbuffer;
  210. ( $res, $readbuffer ) =
  211. S7_ReadBlockFromPLC( $shash, $writeAreaIndex, $dbNR, $byte, 1 );
  212. if ( $res == 0 && length($readbuffer) == 1 ) { #reading was OK
  213. #setzen/löschen wir das gewünsche bit
  214. my $tbuffer = join( ", ", unpack( "H2 " x length($readbuffer), $readbuffer ) );
  215. Log3( undef, 5, "S5 Read old Value <-- " . $tbuffer ." now changing bitNr: ".$bit );
  216. my @cbuffer = unpack( "C" x length($readbuffer), $readbuffer);
  217. if ($b == 1) {
  218. $cbuffer[0] |= (1 << $bit);
  219. } else {
  220. $cbuffer[0] &= (~(1 << $bit)) & 0xFF;
  221. }
  222. $readbuffer = pack( "C" x 1, @cbuffer);
  223. #schreiben wir das byte
  224. $tbuffer = join( ", ", unpack( "H2 " x length($readbuffer), $readbuffer ) );
  225. Log3( undef, 5, "S5 Write new Value <-- " . $tbuffer );
  226. $res = S7_WriteToPLC( $shash, $writeAreaIndex, $dbNR, $byte, &S7Client::S7WLByte , $readbuffer );
  227. if ( $res != 0 ) {
  228. my $error = $shash->{S7PLCClient}->getErrorStr($res);
  229. my $msg =
  230. "$name S7_DWrite_setABit -S5- S7_WriteToPLC error: $res=$error";
  231. Log3( $name, 3, $msg );
  232. }
  233. } else {
  234. my $error = $shash->{S7PLCClient}->getErrorStr($res);
  235. my $msg =
  236. "$name S7_DWrite_setABit -S5- ReadArea error: $res=$error";
  237. Log3( $name, 3, $msg );
  238. S7_reconnect($shash); #lets try a reconnect
  239. return ( -2, $msg );
  240. }
  241. }
  242. else {
  243. #S7
  244. $res =
  245. S7_WriteBitToPLC( $shash, $writeAreaIndex, $dbNR, $position, $b );
  246. }
  247. if ( $res == 0 ) {
  248. main::readingsSingleUpdate( $hash, "state", $newValue, 1 );
  249. }
  250. else {
  251. main::readingsSingleUpdate( $hash, "state", "", 1 );
  252. }
  253. if ( $newValue eq "trigger" ) {
  254. my $triggerLength = 1;
  255. if ( defined( $main::attr{$name}{trigger_length} ) ) {
  256. $triggerLength = $main::attr{$name}{trigger_length};
  257. }
  258. InternalTimer( gettimeofday() + $triggerLength,
  259. "S7_DWrite_SwitchOff", $hash, 1 );
  260. }
  261. return undef;
  262. }
  263. #####################################
  264. sub S7_DWrite_Set(@) {
  265. my ( $hash, @a ) = @_;
  266. return "Need at least one parameter" if ( int(@a) < 2 );
  267. return S7_DWrite_setABit( $hash, $a[1] );
  268. }
  269. #####################################
  270. sub S7_DWrite_SwitchOff($) {
  271. my ($hash) = @_;
  272. my $name = $hash->{NAME};
  273. Log3 $name, 4, "S7_DWrite: GetUpdate called ...";
  274. return S7_DWrite_setABit( $hash, "off" );
  275. }
  276. #####################################
  277. sub S7_DWrite_Parse($$) {
  278. my ( $hash, $rmsg ) = @_;
  279. my $name;
  280. if ( defined( $hash->{NAME} ) ) {
  281. $name = $hash->{NAME};
  282. }
  283. else {
  284. $name = "dummy";
  285. Log3 undef, 2, "S7_DWrite_Parse: Error ...";
  286. return undef;
  287. }
  288. my @a = split( "[ \t][ \t]*", $rmsg );
  289. my @list;
  290. my ( $area, $DB, $start, $length, $datatype, $s7name, $hexbuffer,
  291. $clientNames );
  292. $area = lc $a[1];
  293. $DB = $a[2];
  294. $start = $a[3];
  295. $length = $a[4];
  296. $s7name = $a[5];
  297. $hexbuffer = $a[6];
  298. $clientNames = $a[7];
  299. my $ID = "$area $DB";
  300. Log3 $name, 6, "$name S7_DWrite_Parse $rmsg";
  301. my @clientList = split( ",", $clientNames );
  302. if ( int(@clientList) > 0 ) {
  303. my @Writebuffer = unpack( "C" x $length,
  304. pack( "H2" x $length, split( ",", $hexbuffer ) ) );
  305. foreach my $clientName (@clientList) {
  306. my $h = $defs{$clientName};
  307. if ( $h->{TYPE} eq "S7_DWrite"
  308. && $start <= int( $h->{POSITION} / 8 )
  309. && $start + $length >= int( $h->{POSITION} / 8 ) )
  310. {
  311. push( @list, $clientName )
  312. ; #damit die werte im client gesetzt werden!
  313. #aktualisierung des wertes
  314. my $s = int( $h->{POSITION} / 8 ) - $start;
  315. my $myI = $hash->{S7PLCClient}->ByteAt( \@Writebuffer, $s );
  316. Log3 $name, 5, "$name S7_DWrite_Parse update $clientName ";
  317. if ( ( int($myI) & ( 1 << ( $h->{POSITION} % 8 ) ) ) > 0 ) {
  318. main::readingsSingleUpdate( $h, "state", "on", 1 );
  319. }
  320. else {
  321. main::readingsSingleUpdate( $h, "state", "off", 1 );
  322. }
  323. }
  324. # }
  325. }
  326. }
  327. else {
  328. Log3 $name, 3, "$name S7_DWrite_Parse going the save way ";
  329. if ( defined( $modules{S7_DWrite}{defptr}{$ID} ) ) {
  330. foreach my $h ( @{ $modules{S7_DWrite}{defptr}{$ID} } ) {
  331. if ( defined( $main::attr{ $h->{NAME} }{IODev} )
  332. && $main::attr{ $h->{NAME} }{IODev} eq $name )
  333. {
  334. if ( $start <= int( $h->{POSITION} / 8 )
  335. && $start + $length >= int( $h->{POSITION} / 8 ) )
  336. {
  337. my $n = $h
  338. ->{NAME}; #damit die werte im client gesetzt werden!
  339. push( @list, $n );
  340. #aktualisierung des wertes
  341. my @Writebuffer = unpack(
  342. "C" x $length,
  343. pack(
  344. "H2" x $length, split( ",", $hexbuffer )
  345. )
  346. );
  347. my $s = int( $h->{POSITION} / 8 ) - $start;
  348. # my $b = pack( "C" x $length, @Writebuffer );
  349. my $myI =
  350. $hash->{S7PLCClient}->ByteAt( \@Writebuffer, $s );
  351. Log3 $name, 6, "$name S7_DWrite_Parse update $n ";
  352. if ( ( int($myI) & ( 1 << ( $h->{POSITION} % 8 ) ) )
  353. > 0 )
  354. {
  355. main::readingsSingleUpdate( $h, "state", "on",
  356. 1 );
  357. }
  358. else {
  359. main::readingsSingleUpdate( $h, "state", "off",
  360. 1 );
  361. }
  362. }
  363. }
  364. }
  365. }
  366. }
  367. if ( int(@list) == 0 ) {
  368. Log3 $name, 6, "S7_DWrite: Parse no client found ($name) ...";
  369. push( @list, "" );
  370. }
  371. return @list;
  372. }
  373. #####################################
  374. sub S7_DWrite_Attr(@) {
  375. my ( $cmd, $name, $aName, $aVal ) = @_;
  376. # $cmd can be "del" or "set"
  377. # $name is device name
  378. # aName and aVal are Attribute name and value
  379. my $hash = $defs{$name};
  380. if ( $cmd eq "set" ) {
  381. if ( $aName eq "trigger_length" ) {
  382. if ( $aVal ne int($aVal) ) {
  383. Log3 $name, 3,
  384. "S7_DWrite: Invalid $aName in attr $name $aName ($aVal is not a number): $@";
  385. return "Invalid $aName : $aVal is not a number";
  386. }
  387. }
  388. elsif ( $aName eq "IODev" ) {
  389. Log3 $name, 4, "S7_DWrite: IODev for $name is $aVal";
  390. $hash->{IODev}{dirty} = 1;
  391. }
  392. }
  393. return undef;
  394. }
  395. 1;
  396. =pod
  397. =item summary logical device for a digital writing to a S7/S5
  398. =item summary_DE logisches Device für einen binären Lese/Schreib Datenpunkt zu einer S5 / S7
  399. =begin html
  400. <p><a name="S7_DWrite"></a></p>
  401. <h3>S7_DWrite</h3>
  402. <ul>
  403. <ul>This module is a logical module of the physical module S7.</ul>
  404. </ul>
  405. <ul>
  406. <ul>This module is used to set/unset a Bit in ad DB of the PLC.</ul>
  407. </ul>
  408. <ul>
  409. <ul>Note: you have to configure a PLC writing at the physical modul (S7) first.</ul>
  410. </ul>
  411. <p><br /><br /><br /><strong>Define</strong><code>define &lt;name&gt; S7_DWrite {db} &lt;DB&gt; &lt;address&gt;</code></p>
  412. <ul>
  413. <ul>
  414. <ul>
  415. <ul>
  416. <li>db &hellip; defines where to read. Note currently only writing in to DB are supported.</li>
  417. <li>DB &hellip; Number of the DB</li>
  418. <li>address &hellip; address you want to write. bit number to read. Example: 10.6</li>
  419. </ul>
  420. Note: the required memory area need to be with in the configured PLC reading of the physical module.</ul>
  421. </ul>
  422. </ul>
  423. <p><strong>Set</strong><code>set &lt;name&gt; S7_AWrite {ON|OFF|TRIGGER};</code></p>
  424. <ul>
  425. <ul>Note: TRIGGER sets the bit for 1s to ON than it will set to OFF.</ul>
  426. </ul>
  427. <p><strong>Attr</strong><br /> The following parameters are used to scale every reading</p>
  428. <ul>
  429. <li>trigger_length ... sets the on-time of a trigger</li>
  430. </ul>
  431. =end html
  432. =begin html_DE
  433. <p><a name="S7_DWrite"></a></p>
  434. <h3>S7_DWrite</h3>
  435. <ul>
  436. <ul>This module is a logical module of the physical module S7.</ul>
  437. </ul>
  438. <ul>
  439. <ul>This module is used to set/unset a Bit in ad DB of the PLC.</ul>
  440. </ul>
  441. <ul>
  442. <ul>Note: you have to configure a PLC writing at the physical modul (S7) first.</ul>
  443. </ul>
  444. <p><br /><br /><br /><strong>Define</strong><code>define &lt;name&gt; S7_DWrite {db} &lt;DB&gt; &lt;position&gt;</code></p>
  445. <ul>
  446. <ul>
  447. <ul>
  448. <ul>
  449. <li>db &hellip; defines where to read. Note currently only writing in to DB are supported.</li>
  450. <li>DB &hellip; Number of the DB</li>
  451. <li>address &hellip; address you want to write. bit number to read. Example: 10.6</li>
  452. </ul>
  453. Note: the required memory area need to be with in the configured PLC reading of the physical module.</ul>
  454. </ul>
  455. </ul>
  456. <p><br /><strong>Set</strong><code>set &lt;name&gt; S7_AWrite {ON|OFF|TRIGGER};</code></p>
  457. <ul>
  458. <ul>Note: TRIGGER sets the bit for 1s to ON than it will set to OFF.</ul>
  459. </ul>
  460. <p><strong>Attr</strong><br /> The following parameters are used to scale every reading</p>
  461. <ul>
  462. <li>trigger_length ... sets the on-time of a trigger</li>
  463. </ul>
  464. =end html_DE
  465. =cut