44_S7_ARead.pm 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. # $Id: 44_S7_ARead.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. # "libnodaveversion" => ""
  10. );
  11. sub _isfloat {
  12. my $val = shift;
  13. # return $val =~ m/^\d+.\d+$/;
  14. return $val =~ m/^[-+]?\d*\.?\d*$/;
  15. #[-+]?[0-9]*\.?[0-9]*
  16. }
  17. #####################################
  18. sub S7_ARead_Initialize($) {
  19. my $hash = shift @_;
  20. # Provider
  21. # Consumer
  22. $hash->{Match} = "^AR";
  23. $hash->{DefFn} = "S7_ARead_Define";
  24. $hash->{UndefFn} = "S7_ARead_Undef";
  25. $hash->{ParseFn} = "S7_ARead_Parse";
  26. $hash->{AttrFn} = "S7_ARead_Attr";
  27. $hash->{AttrList} = "IODev offset multiplicator " . $readingFnAttributes;
  28. main::LoadModule("S7");
  29. }
  30. #####################################
  31. sub S7_ARead_Define($$) {
  32. my ( $hash, $def ) = @_;
  33. my @a = split( "[ \t][ \t]*", $def );
  34. my ( $name, $area, $DB, $start, $datatype );
  35. $name = $a[0];
  36. $area = lc $a[2];
  37. $DB = $a[3];
  38. $start = $a[4];
  39. $datatype = lc $a[5];
  40. if ( $area ne "inputs"
  41. && $area ne "outputs"
  42. && $area ne "flags"
  43. && $area ne "db" )
  44. {
  45. my $msg =
  46. "wrong syntax: define <name> S7_ARead {inputs|outputs|flags|db} <DB> <start> {u8|s8|u16|s16|u32|s32|float}";
  47. Log3 undef, 2, $msg;
  48. return $msg;
  49. }
  50. if ( $datatype ne "u8"
  51. && $datatype ne "s8"
  52. && $datatype ne "u16"
  53. && $datatype ne "s16"
  54. && $datatype ne "u32"
  55. && $datatype ne "s32"
  56. && $datatype ne "float" )
  57. {
  58. my $msg =
  59. "wrong syntax: define <name> S7_ARead {inputs|outputs|flags|db} <DB> <start> {u8|s8|u16|s16|u32|s32|float}";
  60. Log3 undef, 2, $msg;
  61. return $msg;
  62. }
  63. $hash->{AREA} = $area;
  64. $hash->{DB} = $DB;
  65. $hash->{ADDRESS} = $start;
  66. $hash->{DATATYPE} = $datatype;
  67. if ( $datatype eq "u16" || $datatype eq "s16" ) {
  68. $hash->{LENGTH} = 2;
  69. }
  70. elsif ( $datatype eq "u32" || $datatype eq "s32" || $datatype eq "float" ) {
  71. $hash->{LENGTH} = 4;
  72. }
  73. else {
  74. $hash->{LENGTH} = 1;
  75. }
  76. my $ID = "$area $DB";
  77. if ( !defined( $modules{S7_ARead}{defptr}{$ID} ) ) {
  78. my @b = ();
  79. push( @b, $hash );
  80. $modules{S7_ARead}{defptr}{$ID} = \@b;
  81. }
  82. else {
  83. push( @{ $modules{S7_ARead}{defptr}{$ID} }, $hash );
  84. }
  85. AssignIoPort($hash); # logisches modul an physikalisches binden !!!
  86. $hash->{IODev}{dirty} = 1;
  87. Log3 $name, 4,
  88. "S7_ARead (" . $hash->{IODev}{NAME} . "): define $name Adress:$start";
  89. return undef;
  90. }
  91. #####################################
  92. sub S7_ARead_Undef($$) {
  93. my ( $hash, $name ) = @_;
  94. Log3 $name, 4,
  95. "S7_ARead ("
  96. . $hash->{IODev}{NAME}
  97. . "): undef "
  98. . $hash->{NAME}
  99. . " Adress:"
  100. . $hash->{ADDRESS};
  101. delete( $modules{S7_ARead}{defptr} );
  102. return undef;
  103. }
  104. #####################################
  105. sub S7_ARead_Parse($$) {
  106. my ( $hash, $rmsg ) = @_;
  107. my $name = $hash->{NAME};
  108. my @a = split( "[ \t][ \t]*", $rmsg );
  109. my @list;
  110. my ( $area, $DB, $start, $length, $datatype, $s7name, $hexbuffer,
  111. $clientNames );
  112. $area = lc $a[1];
  113. $DB = $a[2];
  114. $start = $a[3];
  115. $length = $a[4];
  116. $s7name = $a[5];
  117. $hexbuffer = $a[6];
  118. $clientNames = $a[7];
  119. my $ID = "$area $DB";
  120. Log3 $name, 5, "$name S7_ARead_Parse $rmsg";
  121. my @clientList = split( ",", $clientNames );
  122. if ( int(@clientList) > 0 ) {
  123. my @Writebuffer = unpack( "C" x $length,
  124. pack( "H2" x $length, split( ",", $hexbuffer ) ) );
  125. #my $b = pack( "C" x $length, @Writebuffer );
  126. foreach my $clientName (@clientList) {
  127. my $h = $defs{$clientName};
  128. if ( $h->{TYPE} eq "S7_ARead"
  129. && $start <= $h->{ADDRESS}
  130. && $start + $length >= $h->{ADDRESS} + $h->{LENGTH} )
  131. {
  132. my $n = $h->{NAME}; #damit die werte im client gesetzt werden!
  133. push( @list, $n );
  134. #aktualisierung des wertes
  135. my $s = $h->{ADDRESS} - $start;
  136. my $myI;
  137. if ( $h->{DATATYPE} eq "u8" ) {
  138. $myI = $hash->{S7PLCClient}->ByteAt( \@Writebuffer, $s );
  139. }
  140. elsif ( $h->{DATATYPE} eq "s8" ) {
  141. $myI = $hash->{S7PLCClient}->ShortAt( \@Writebuffer, $s );
  142. }
  143. elsif ( $h->{DATATYPE} eq "u16" ) {
  144. $myI = $hash->{S7PLCClient}->WordAt( \@Writebuffer, $s );
  145. }
  146. elsif ( $h->{DATATYPE} eq "s16" ) {
  147. $myI = $hash->{S7PLCClient}->IntegerAt( \@Writebuffer, $s );
  148. }
  149. elsif ( $h->{DATATYPE} eq "u32" ) {
  150. $myI = $hash->{S7PLCClient}->DWordAt( \@Writebuffer, $s );
  151. }
  152. elsif ( $h->{DATATYPE} eq "s32" ) {
  153. $myI = $hash->{S7PLCClient}->DintAt( \@Writebuffer, $s );
  154. }
  155. elsif ( $h->{DATATYPE} eq "float" ) {
  156. $myI = $hash->{S7PLCClient}->FloatAt( \@Writebuffer, $s );
  157. }
  158. else {
  159. Log3 $name, 3,
  160. "$name S7_ARead: Parse unknown type : ("
  161. . $h->{DATATYPE} . ")";
  162. }
  163. #now we need to correct the analog value by the parameters attribute and offset
  164. my $offset = 0;
  165. if ( defined( $main::attr{$n}{offset} ) ) {
  166. $offset = $main::attr{$n}{offset};
  167. }
  168. my $multi = 1;
  169. if ( defined( $main::attr{$n}{multiplicator} ) ) {
  170. $multi = $main::attr{$n}{multiplicator};
  171. }
  172. $myI = $myI * $multi + $offset;
  173. #my $myResult;
  174. main::readingsSingleUpdate( $h, "state", $myI, 1 );
  175. # main::readingsSingleUpdate($h,"value",$myResult, 1);
  176. }
  177. }
  178. }
  179. else {
  180. Log3 $name, 3, "$name S7_ARead_Parse going the save way ";
  181. if ( defined( $modules{S7_ARead}{defptr}{$ID} ) ) {
  182. foreach my $h ( @{ $modules{S7_ARead}{defptr}{$ID} } ) {
  183. if ( defined( $main::attr{ $h->{NAME} }{IODev} )
  184. && $main::attr{ $h->{NAME} }{IODev} eq $name )
  185. {
  186. if ( $start <= $h->{ADDRESS}
  187. && $start + $length >= $h->{ADDRESS} + $h->{LENGTH} )
  188. {
  189. my $n =
  190. $h->{NAME}; #damit die werte im client gesetzt werden!
  191. push( @list, $n );
  192. #aktualisierung des wertes
  193. my @Writebuffer = unpack( "C" x $length,
  194. pack( "H2" x $length, split( ",", $hexbuffer ) ) );
  195. my $s = $h->{ADDRESS} - $start;
  196. #my $b = pack( "C" x $length, @Writebuffer );
  197. my $myI;
  198. if ( $h->{DATATYPE} eq "u8" ) {
  199. $myI =
  200. $hash->{S7PLCClient}->ByteAt( \@Writebuffer, $s );
  201. }
  202. elsif ( $h->{DATATYPE} eq "s8" ) {
  203. $myI =
  204. $hash->{S7PLCClient}
  205. ->ShortAt( \@Writebuffer, $s );
  206. }
  207. elsif ( $h->{DATATYPE} eq "u16" ) {
  208. $myI =
  209. $hash->{S7PLCClient}->WordAt( \@Writebuffer, $s );
  210. }
  211. elsif ( $h->{DATATYPE} eq "s16" ) {
  212. $myI =
  213. $hash->{S7PLCClient}
  214. ->IntegerAt( \@Writebuffer, $s );
  215. }
  216. elsif ( $h->{DATATYPE} eq "u32" ) {
  217. $myI =
  218. $hash->{S7PLCClient}
  219. ->DWordAt( \@Writebuffer, $s );
  220. }
  221. elsif ( $h->{DATATYPE} eq "s32" ) {
  222. $myI =
  223. $hash->{S7PLCClient}->DintAt( \@Writebuffer, $s );
  224. }
  225. elsif ( $h->{DATATYPE} eq "float" ) {
  226. $myI =
  227. $hash->{S7PLCClient}
  228. ->FloatAt( \@Writebuffer, $s );
  229. }
  230. else {
  231. Log3 $name, 3,
  232. "$name S7_ARead: Parse unknown type : ("
  233. . $h->{DATATYPE} . ")";
  234. }
  235. #now we need to correct the analog value by the parameters attribute and offset
  236. my $offset = 0;
  237. if ( defined( $main::attr{$n}{offset} ) ) {
  238. $offset = $main::attr{$n}{offset};
  239. }
  240. my $multi = 1;
  241. if ( defined( $main::attr{$n}{multiplicator} ) ) {
  242. $multi = $main::attr{$n}{multiplicator};
  243. }
  244. $myI = $myI * $multi + $offset;
  245. #my $myResult;
  246. main::readingsSingleUpdate( $h, "state", $myI, 1 );
  247. # main::readingsSingleUpdate($h,"value",$myResult, 1);
  248. }
  249. }
  250. }
  251. }
  252. }
  253. if ( int(@list) == 0 ) {
  254. Log3 $name, 6, "S7_ARead: Parse no client found ($name) ...";
  255. push( @list, "" );
  256. }
  257. return @list;
  258. }
  259. #####################################
  260. sub S7_ARead_Attr(@) {
  261. my ( $cmd, $name, $aName, $aVal ) = @_;
  262. # $cmd can be "del" or "set"
  263. # $name is device name
  264. # aName and aVal are Attribute name and value
  265. my $hash = $defs{$name};
  266. if ( $cmd eq "set" ) {
  267. if ( $aName eq "offset" || $aName eq "multiplicator" ) {
  268. if ( !_isfloat($aVal) ) {
  269. Log3 $name, 3,
  270. "S7_ARead: Invalid $aName in attr $name $aName $aVal ($aVal is not a number): $@";
  271. return "Invalid $aName $aVal: $aVal is not a number";
  272. }
  273. }
  274. elsif ( $aName eq "IODev" ) {
  275. if ( defined( $hash->{IODev} ) ) { #set old master device dirty
  276. $hash->{IODev}{dirty} = 1;
  277. }
  278. if ( defined( $defs{$aVal} ) ) { #set new master device dirty
  279. $defs{$aVal}{dirty} = 1;
  280. }
  281. Log3 $name, 4, "S7_ARead: IODev for $name is $aVal";
  282. }
  283. }
  284. return undef;
  285. }
  286. 1;
  287. =pod
  288. =item summary logical device for a analog reading from a S7/S5
  289. =item summary_DE logisches Device für einen analogen Nur Lese Datenpunkt von einer S5 / S7
  290. =begin html
  291. <a name="S7_ARead"></a>
  292. <h3>S7_ARead</h3>
  293. <ul>
  294. This module is a logical module of the physical module S7. <br>
  295. This module provides analog data (signed / unsigned integer Values).<br>
  296. Note: you have to configure a PLC reading at the physical module (S7) first.<br>
  297. <br><br>
  298. <b>Define</b><br>
  299. <code>define &lt;name&gt; S7_ARead {inputs|outputs|flags|db} &lt;DB&gt; &lt;start&gt; {u8|s8|u16|s16|u32|s32}</code>
  300. <br><br>
  301. <ul>
  302. <li>inputs|outputs|flags|db … defines where to read.</li>
  303. <li>DB … Number of the DB</li>
  304. <li>start … start byte of the reading</li>
  305. <li>{u8|s8|u16|s16|u32|s32} … defines the datatype: </li>
  306. <ul>
  307. <li>u8 …. unsigned 8 Bit integer</li>
  308. <li>s8 …. signed 8 Bit integer</li>
  309. <li>u16 …. unsigned 16 Bit integer</li>
  310. <li>s16 …. signed 16 Bit integer</li>
  311. <li>u32 …. unsigned 32 Bit integer</li>
  312. <li>s32 …. signed 32 Bit integer</li>
  313. </ul>
  314. Note: the required memory area (start – start + datatypelength) need to be with in the configured PLC reading of the physical module.
  315. </ul>
  316. <br>
  317. <b>Attr</b><br>
  318. The following parameters are used to scale every reading<br>
  319. <ul>
  320. <li>multiplicator</li>
  321. <li>offset</li>
  322. </ul>
  323. newValue = &lt;multiplicator&gt; * Value + &lt;offset&gt;
  324. </ul>
  325. =end html
  326. =begin html_DE
  327. <a name="S7_ARead"></a>
  328. <h3>S7_ARead</h3>
  329. <ul>
  330. This module is a logical module of the physical module S7. <br>
  331. This module provides analog data (signed / unsigned integer Values).<br>
  332. Note: you have to configure a PLC reading at the physical module (S7) first.<br>
  333. <br><br>
  334. <b>Define</b><br>
  335. <code>define &lt;name&gt; S7_ARead {inputs|outputs|flags|db} &lt;DB&gt; &lt;start&gt; {u8|s8|u16|s16|u32|s32}</code>
  336. <br><br>
  337. <ul>
  338. <li>inputs|outputs|flags|db … defines where to read.</li>
  339. <li>DB … Number of the DB</li>
  340. <li>start … start byte of the reading</li>
  341. <li>{u8|s8|u16|s16|u32|s32} … defines the datatype: </li>
  342. <ul>
  343. <li>u8 …. unsigned 8 Bit integer</li>
  344. <li>s8 …. signed 8 Bit integer</li>
  345. <li>u16 …. unsigned 16 Bit integer</li>
  346. <li>s16 …. signed 16 Bit integer</li>
  347. <li>u32 …. unsigned 32 Bit integer</li>
  348. <li>s32 …. signed 32 Bit integer</li>
  349. <li>float …. 4 byte float </li>
  350. </ul>
  351. Note: the required memory area (start – start + datatypelength) need to be with in the configured PLC reading of the physical module.
  352. </ul>
  353. <b>Attr</b>
  354. The following parameters are used to scale every reading
  355. <ul>
  356. <li>multiplicator</li>
  357. <li>offset</li>
  358. </ul>
  359. newValue = &lt;multiplicator&gt; * Value + &lt;offset&gt;
  360. </ul>
  361. =end html_DE
  362. =cut