44_S7_AWrite.pm 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572
  1. # $Id: 44_S7_AWrite.pm 12776 2016-12-14 18:09:08Z charlie71born $
  2. ##############################################
  3. package main;
  4. use strict;
  5. use warnings;
  6. #use Switch;
  7. require "44_S7_Client.pm";
  8. my %gets = (
  9. "reading" => "",
  10. "STATE" => ""
  11. );
  12. #####################################
  13. sub S7_AWrite_Initialize($) {
  14. my $hash = shift @_;
  15. # Consumer
  16. $hash->{Match} = "^AW";
  17. $hash->{DefFn} = "S7_AWrite_Define";
  18. $hash->{UndefFn} = "S7_AWrite_Undef";
  19. # $hash->{GetFn} = "S7_AWrite_Get";
  20. $hash->{SetFn} = "S7_AWrite_Set";
  21. $hash->{ParseFn} = "S7_AWrite_Parse";
  22. $hash->{AttrFn} = "S7_AWrite_Attr";
  23. $hash->{AttrList} = "IODev " . $readingFnAttributes;
  24. main::LoadModule("S7");
  25. }
  26. #####################################
  27. sub S7_AWrite_Define($$) {
  28. my ( $hash, $def ) = @_;
  29. my @a = split( "[ \t][ \t]*", $def );
  30. my ( $name, $area, $DB, $start, $datatype, $length );
  31. $name = $a[0];
  32. $area = lc $a[2];
  33. $DB = $a[3];
  34. $start = $a[4];
  35. $datatype = lc $a[5];
  36. Log3 $name, 5, "$name S7_AWrite_Define called";
  37. if ( $area ne "inputs"
  38. && $area ne "outputs"
  39. && $area ne "flags"
  40. && $area ne "db" )
  41. {
  42. my $msg =
  43. "$name wrong syntax: define <name> S7_AWrite {inputs|outputs|flags|db} <DB> <start> {u8|s8|u16|s16|u32|s32|float}";
  44. Log3 $name, 2, $msg;
  45. return $msg;
  46. }
  47. if ( $datatype ne "u8"
  48. && $datatype ne "s8"
  49. && $datatype ne "u16"
  50. && $datatype ne "s16"
  51. && $datatype ne "u32"
  52. && $datatype ne "s32"
  53. && $datatype ne "float" )
  54. {
  55. my $msg =
  56. "$name wrong syntax: define <name> S7_AWrite {inputs|outputs|flags|db} <DB> <start> {u8|s8|u16|s16|u32|s32|float}";
  57. Log3 $name, 2, $msg;
  58. return $msg;
  59. }
  60. AssignIoPort($hash); # logisches modul an physikalisches binden !!!
  61. my $sname = $hash->{IODev}{NAME};
  62. if ( $datatype eq "u16" || $datatype eq "s16" ) {
  63. $length = 2;
  64. }
  65. elsif ( $datatype eq "u32" || $datatype eq "s32" || $datatype eq "float" ) {
  66. $length = 4;
  67. }
  68. else {
  69. $length = 1;
  70. }
  71. $hash->{AREA} = $area;
  72. $hash->{DB} = $DB;
  73. $hash->{ADDRESS} = $start;
  74. $hash->{DATATYPE} = $datatype;
  75. $hash->{LENGTH} = $length;
  76. my $ID = "$area $DB";
  77. if ( !defined( $modules{S7_AWrite}{defptr}{$ID} ) ) {
  78. my @b = ();
  79. push( @b, $hash );
  80. $modules{S7_AWrite}{defptr}{$ID} = \@b;
  81. }
  82. else {
  83. push( @{ $modules{S7_AWrite}{defptr}{$ID} }, $hash );
  84. }
  85. Log3 $name, 4,
  86. "S7_AWrite (" . $hash->{IODev}{NAME} . "): define $name Adress:$start";
  87. $hash->{IODev}{dirty} = 1;
  88. return undef;
  89. }
  90. #####################################
  91. sub S7_AWrite_Undef($$) {
  92. my ( $hash, $name ) = @_;
  93. Log3 $name, 4,
  94. "S7_AWrite ("
  95. . $hash->{IODev}{NAME}
  96. . "): undef "
  97. . $hash->{NAME}
  98. . " Adress:"
  99. . $hash->{ADDRESS};
  100. delete( $modules{S7_AWrite}{defptr} );
  101. return undef;
  102. }
  103. #####################################
  104. sub S7_AWrite_Set($@) {
  105. my ( $hash, @a ) = @_;
  106. my $name = $hash->{NAME};
  107. Log3 $name, 5, "$name S7_AWrite_Set";
  108. my $minValue;
  109. my $maxValue;
  110. my $datatype = $hash->{DATATYPE};
  111. #note I have used a SIEMENS Logo for testing here just the following range was supported.
  112. # $minValue = 0;
  113. # $maxValue = 32767;
  114. if ( $datatype eq "u16" ) {
  115. $minValue = 0;
  116. $maxValue = 65535;
  117. }
  118. elsif ( $datatype eq "s16" ) {
  119. $minValue = -32768;
  120. $maxValue = 32767;
  121. }
  122. elsif ( $datatype eq "u32" ) {
  123. $minValue = 0;
  124. $maxValue = 4294967295;
  125. }
  126. elsif ( $datatype eq "s32" ) {
  127. $minValue = -2147483648;
  128. $maxValue = 2147483647;
  129. }
  130. elsif ( $datatype eq "float" ) {
  131. $minValue = -3.402823e38;
  132. $maxValue = 3.402823e38;
  133. }
  134. elsif ( $datatype eq "u8" ) {
  135. $minValue = 0;
  136. $maxValue = 255;
  137. }
  138. elsif ( $datatype eq "s8" ) {
  139. $minValue = -128;
  140. $maxValue = 127;
  141. }
  142. else { #should never happen
  143. $minValue = -1;
  144. $maxValue = 0;
  145. }
  146. return "$name Need at least one parameter" if ( int(@a) < 2 );
  147. return " : " if ( $a[1] eq "?" );
  148. if ( $a[1] ne int( $a[1] ) && $datatype ne "float" ) {
  149. return "$name You have to enter a numeric value: $minValue - $maxValue";
  150. }
  151. my $newValue;
  152. if ( $datatype ne "float" ) {
  153. $newValue = int( $a[1] );
  154. }
  155. else {
  156. $newValue = $a[1];
  157. }
  158. if ( $newValue < $minValue || $newValue > $maxValue ) {
  159. return "$name Out of range: $minValue - $maxValue";
  160. }
  161. my $sname = $hash->{IODev}{NAME};
  162. #find the rigth config
  163. my $area = $hash->{AREA};
  164. my $length = $hash->{LENGTH};
  165. my $start = $hash->{ADDRESS};
  166. my $dbNR = $hash->{DB};
  167. my $shash = $defs{$sname};
  168. if ( !defined( $shash->{S7PLCClient} ) ) {
  169. my $err = "$name S7_AWrite_Set: not connected to PLC ";
  170. Log3 $name, 3, $err;
  171. return $err;
  172. }
  173. if ( $shash->{STATE} ne "connected to PLC" ) {
  174. my $err = "$name S7_AWrite_Set: not connected to PLC";
  175. Log3 $name, 3, $err;
  176. return $err;
  177. }
  178. my $b;
  179. my $WordLen;
  180. if ( $datatype eq "u8" ) {
  181. $b = $shash->{S7PLCClient}->setByteAt( "X", 0, $newValue );
  182. $WordLen = &S7Client::S7WLByte;
  183. }
  184. elsif ( $datatype eq "s8" ) {
  185. $b = $shash->{S7PLCClient}->setShortAt( "X", 0, $newValue );
  186. $WordLen = &S7Client::S7WLByte;
  187. }
  188. elsif ( $datatype eq "u16" ) {
  189. $b = $shash->{S7PLCClient}->setWordAt( "XX", 0, $newValue );
  190. $WordLen = &S7Client::S7WLInt;
  191. # $WordLen = &S7Client::S7WLWord;
  192. }
  193. elsif ( $datatype eq "s16" ) {
  194. $b = $shash->{S7PLCClient}->setIntegerAt( "XX", 0, $newValue );
  195. $WordLen = &S7Client::S7WLInt;
  196. # $WordLen = &S7Client::S7WLWord;
  197. }
  198. elsif ( $datatype eq "u32" ) {
  199. $b = $shash->{S7PLCClient}->setDWordAt( "XXXX", 0, $newValue );
  200. $WordLen = &S7Client::S7WLDInt;
  201. # $WordLen = &S7Client::S7WLDWord;
  202. }
  203. elsif ( $datatype eq "s32" ) {
  204. $b = $shash->{S7PLCClient}->setDintAt( "XXXX", 0, $newValue );
  205. $WordLen = &S7Client::S7WLDInt;
  206. # $WordLen = &S7Client::S7WLDWord;
  207. }
  208. elsif ( $datatype eq "float" ) {
  209. $b = $shash->{S7PLCClient}->setFloatAt( "XXXX", 0, $newValue );
  210. $WordLen = &S7Client::S7WLReal;
  211. }
  212. else {
  213. my $err = "$name S7_AWrite: Parse unknown type : (" . $datatype . ")";
  214. Log3 $name, 3, $err;
  215. return $err;
  216. }
  217. my $bss = join( ", ", unpack( "H2" x $length, $b ) );
  218. Log3 $name, 5, "$name S7_AWrite_Set: Write Bytes to PLC: $bss";
  219. my $writeAreaIndex = S7_getAreaIndex4AreaName($area);
  220. return $writeAreaIndex if ( $writeAreaIndex ne int($writeAreaIndex) );
  221. # my $res = S7_WriteBlockToPLC($shash,$writeAreaIndex,$dbNR,$start,$b);
  222. my $res =
  223. S7_WriteToPLC( $shash, $writeAreaIndex, $dbNR, $start, $WordLen, $b );
  224. if ( $res == 0 ) {
  225. main::readingsSingleUpdate( $hash, "state", $newValue, 1 );
  226. }
  227. else {
  228. main::readingsSingleUpdate( $hash, "state", "", 1 );
  229. }
  230. return undef;
  231. }
  232. #####################################
  233. sub S7_AWrite_Parse($$) {
  234. my ( $hash, $rmsg ) = @_;
  235. my $name = $hash->{NAME};
  236. my @list;
  237. my @a = split( "[ \t][ \t]*", $rmsg );
  238. my ( $area, $DB, $start, $length, $datatype, $s7name, $hexbuffer,
  239. $clientNames );
  240. $area = lc $a[1];
  241. $DB = $a[2];
  242. $start = $a[3];
  243. $length = $a[4];
  244. $s7name = $a[5];
  245. $hexbuffer = $a[6];
  246. $clientNames = $a[7];
  247. my $ID = "$area $DB";
  248. Log3 $name, 5, "$name S7_AWrite_Parse $rmsg";
  249. my @clientList = split( ",", $clientNames );
  250. if ( int(@clientList) > 0 ) {
  251. my @Writebuffer = unpack( "C" x $length,
  252. pack( "H2" x $length, split( ",", $hexbuffer ) ) );
  253. #my $b = pack( "C" x $length, @Writebuffer );
  254. foreach my $clientName (@clientList) {
  255. my $h = $defs{$clientName};
  256. if ( $h->{TYPE} eq "S7_AWrite"
  257. && $start <= $h->{ADDRESS}
  258. && $start + $length >= $h->{ADDRESS} + $h->{LENGTH} )
  259. {
  260. my $n = $h->{NAME}; #damit die werte im client gesetzt werden!
  261. push( @list, $n );
  262. #Aktualisierung des wertes
  263. my $s = $h->{ADDRESS} - $start;
  264. my $myI;
  265. if ( $h->{DATATYPE} eq "u8" ) {
  266. $myI = $hash->{S7PLCClient}->ByteAt( \@Writebuffer, $s );
  267. }
  268. elsif ( $h->{DATATYPE} eq "s8" ) {
  269. $myI = $hash->{S7PLCClient}->ShortAt( \@Writebuffer, $s );
  270. }
  271. elsif ( $h->{DATATYPE} eq "u16" ) {
  272. $myI = $hash->{S7PLCClient}->WordAt( \@Writebuffer, $s );
  273. }
  274. elsif ( $h->{DATATYPE} eq "s16" ) {
  275. $myI = $hash->{S7PLCClient}->IntegerAt( \@Writebuffer, $s );
  276. }
  277. elsif ( $h->{DATATYPE} eq "u32" ) {
  278. $myI = $hash->{S7PLCClient}->DWordAt( \@Writebuffer, $s );
  279. }
  280. elsif ( $h->{DATATYPE} eq "s32" ) {
  281. $myI = $hash->{S7PLCClient}->DintAt( \@Writebuffer, $s );
  282. }
  283. elsif ( $h->{DATATYPE} eq "float" ) {
  284. $myI = $hash->{S7PLCClient}->FloatAt( \@Writebuffer, $s );
  285. }
  286. else {
  287. Log3 $name, 3, "$name S7_AWrite: Parse unknown type : ("
  288. . $h->{DATATYPE} . ")";
  289. }
  290. main::readingsSingleUpdate( $h, "state", $myI, 1 );
  291. }
  292. }
  293. }
  294. else {
  295. Log3 $name, 3, "$name S7_AWrite_Parse going the save way ";
  296. if ( defined( $modules{S7_AWrite}{defptr}{$ID} ) ) {
  297. foreach my $h ( @{ $modules{S7_AWrite}{defptr}{$ID} } ) {
  298. if ( defined( $main::attr{ $h->{NAME} }{IODev} )
  299. && $main::attr{ $h->{NAME} }{IODev} eq $name )
  300. {
  301. if ( $start <= $h->{ADDRESS}
  302. && $start + $length >= $h->{ADDRESS} + $h->{LENGTH} )
  303. {
  304. my $n =
  305. $h->{NAME}; #damit die werte im client gesetzt werden!
  306. push( @list, $n );
  307. #Aktualisierung des wertes
  308. my @Writebuffer = unpack( "C" x $length,
  309. pack( "H2" x $length, split( ",", $hexbuffer ) ) );
  310. my $s = $h->{ADDRESS} - $start;
  311. # my $b = pack( "C" x $length, @Writebuffer );
  312. my $myI;
  313. if ( $h->{DATATYPE} eq "u8" ) {
  314. $myI =
  315. $hash->{S7PLCClient}->ByteAt( \@Writebuffer, $s );
  316. }
  317. elsif ( $h->{DATATYPE} eq "s8" ) {
  318. $myI =
  319. $hash->{S7PLCClient}
  320. ->ShortAt( \@Writebuffer, $s );
  321. }
  322. elsif ( $h->{DATATYPE} eq "u16" ) {
  323. $myI =
  324. $hash->{S7PLCClient}->WordAt( \@Writebuffer, $s );
  325. }
  326. elsif ( $h->{DATATYPE} eq "s16" ) {
  327. $myI =
  328. $hash->{S7PLCClient}
  329. ->IntegerAt( \@Writebuffer, $s );
  330. }
  331. elsif ( $h->{DATATYPE} eq "u32" ) {
  332. $myI =
  333. $hash->{S7PLCClient}
  334. ->DWordAt( \@Writebuffer, $s );
  335. }
  336. elsif ( $h->{DATATYPE} eq "s32" ) {
  337. $myI =
  338. $hash->{S7PLCClient}->DintAt( \@Writebuffer, $s );
  339. }
  340. elsif ( $h->{DATATYPE} eq "float" ) {
  341. $myI =
  342. $hash->{S7PLCClient}
  343. ->FloatAt( \@Writebuffer, $s );
  344. }
  345. else {
  346. Log3 $name, 3,
  347. "$name S7_AWrite: Parse unknown type : ("
  348. . $h->{DATATYPE} . ")";
  349. }
  350. main::readingsSingleUpdate( $h, "state", $myI, 1 );
  351. }
  352. }
  353. }
  354. }
  355. }
  356. if ( int(@list) == 0 ) {
  357. Log3 $name, 5, "S7_AWrite: Parse no client found ($name) ...";
  358. push( @list, "" );
  359. # return undef;
  360. }
  361. return @list;
  362. }
  363. #####################################
  364. sub S7_AWrite_Attr(@) {
  365. my ( $cmd, $name, $aName, $aVal ) = @_;
  366. # $cmd can be "del" or "set"
  367. # $name is device name
  368. # aName and aVal are Attribute name and value
  369. my $hash = $defs{$name};
  370. if ( $cmd eq "set" ) {
  371. if ( $aName eq "IODev" ) {
  372. if ( defined( $hash->{IODev} ) ) { #set old master device dirty
  373. $hash->{IODev}{dirty} = 1;
  374. }
  375. if ( defined( $defs{$aVal} ) ) { #set new master device dirty
  376. $defs{$aVal}{dirty} = 1;
  377. }
  378. Log3 $name, 4, "S7_AWrite: IODev for $name is $aVal";
  379. }
  380. }
  381. return undef;
  382. }
  383. 1;
  384. =pod
  385. =item summary logical device for a analog writing to a S7/S5
  386. =item summary_DE logisches Device für einen analogen Lese/Schreib Datenpunkt zu einer S5 / S7
  387. =begin html
  388. <p><a name="S7_AWrite"></a></p>
  389. <h3>S7_AWrite</h3>
  390. <ul>
  391. <ul>This module is a logical module of the physical module S7.</ul>
  392. </ul>
  393. <ul>
  394. <ul>This module provides sending analog data (unsigned integer Values) to the PLC.</ul>
  395. </ul>
  396. <ul>
  397. <ul>Note: you have to configure a PLC writing at the physical modul (S7) first.</ul>
  398. </ul>
  399. <p><br /><br /><strong>Define</strong><br /><code>define &lt;name&gt; S7_AWrite {inputs|outputs|flags|db} &lt;DB&gt; &lt;start&gt; {u8|s8|u16|s16|u32|s32|float}</code><br /><br /></p>
  400. <ul>
  401. <ul>
  402. <ul>
  403. <ul>
  404. <li>db &hellip; defines where to read. Note currently only writing in to DB are supported.</li>
  405. <li>DB &hellip; Number of the DB</li>
  406. <li>start &hellip; start byte of the reading</li>
  407. <li>{u8|s8|u16|s16|u32|s32} &hellip; defines the datatype:</li>
  408. <ul>
  409. <li>u8 &hellip;. unsigned 8 Bit integer</li>
  410. <li>s8 &hellip;. signed 8 Bit integer</li>
  411. <li>u16 &hellip;. unsigned 16 Bit integer</li>
  412. <li>s16 &hellip;. signed 16 Bit integer</li>
  413. <li>u32 &hellip;. unsigned 32 Bit integer</li>
  414. <li>s32 &hellip;. signed 32 Bit integer</li>
  415. <li>float &hellip;. 4 byte float</li>
  416. </ul>
  417. </ul>
  418. Note: the required memory area (start &ndash; start + datatypelength) need to be with in the configured PLC writing of the physical module.</ul>
  419. </ul>
  420. </ul>
  421. <p><strong>Set</strong><br /><br /><code>set &lt;name&gt; S7_AWrite &lt;value&gt;</code></p>
  422. <ul>
  423. <ul>
  424. <ul>
  425. <li>value &hellip; an numeric value</li>
  426. </ul>
  427. </ul>
  428. </ul>
  429. =end html
  430. =begin html_DE
  431. <p><a name="S7_AWrite"></a></p>
  432. <h3>S7_AWrite</h3>
  433. <ul>
  434. <ul>This module is a logical module of the physical module S7.</ul>
  435. </ul>
  436. <ul>
  437. <ul>This module provides sending analog data (unsigned integer Values) to the PLC.</ul>
  438. </ul>
  439. <ul>
  440. <ul>Note: you have to configure a PLC writing at the physical modul (S7) first.</ul>
  441. </ul>
  442. <p><br /><br /><strong>Define</strong><br /><code>define &lt;name&gt; S7_AWrite {inputs|outputs|flags|db} &lt;DB&gt; &lt;start&gt; {u8|s8|u16|s16|u32|s32|float}</code><br /><br /></p>
  443. <ul>
  444. <ul>
  445. <ul>
  446. <ul>
  447. <li>db &hellip; defines where to read. Note currently only writing in to DB are supported.</li>
  448. <li>DB &hellip; Number of the DB</li>
  449. <li>start &hellip; start byte of the reading</li>
  450. <li>{u8|s8|u16|s16|u32|s32} &hellip; defines the datatype:</li>
  451. <ul>
  452. <li>u8 &hellip;. unsigned 8 Bit integer</li>
  453. <li>s8 &hellip;. signed 8 Bit integer</li>
  454. <li>u16 &hellip;. unsigned 16 Bit integer</li>
  455. <li>s16 &hellip;. signed 16 Bit integer</li>
  456. <li>u32 &hellip;. unsigned 32 Bit integer</li>
  457. <li>s32 &hellip;. signed 32 Bit integer</li>
  458. <li>float &hellip;. 4 byte float</li>
  459. </ul>
  460. </ul>
  461. Note: the required memory area (start &ndash; start + datatypelength) need to be with in the configured PLC writing of the physical module.</ul>
  462. </ul>
  463. </ul>
  464. <p><strong>Set</strong><br /><br /><code>set &lt;name&gt; S7_AWrite &lt;value&gt;</code></p>
  465. <ul>
  466. <ul>
  467. <ul>
  468. <li>value &hellip; an numeric value</li>
  469. </ul>
  470. </ul>
  471. </ul>=end html_DE
  472. =cut