93_DbRep.pm 220 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676
  1. ##########################################################################################################
  2. # $Id: 93_DbRep.pm 13335 2017-02-05 13:50:08Z DS_Starter $
  3. ##########################################################################################################
  4. # 93_DbRep.pm
  5. #
  6. # (c) 2016-2017 by Heiko Maaz
  7. # e-mail: Heiko dot Maaz at t-online dot de
  8. #
  9. # This Module can be used to select and report content of databases written by 93_DbLog module
  10. # in different manner.
  11. #
  12. # This script is part of fhem.
  13. #
  14. # Fhem is free software: you can redistribute it and/or modify
  15. # it under the terms of the GNU General Public License as published by
  16. # the Free Software Foundation, either version 2 of the License, or
  17. # (at your option) any later version.
  18. #
  19. # Fhem is distributed in the hope that it will be useful,
  20. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  21. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  22. # GNU General Public License for more details.
  23. #
  24. # You should have received a copy of the GNU General Public License
  25. # along with fhem. If not, see <http://www.gnu.org/licenses/>.
  26. #
  27. # Credits:
  28. # - some proposals to boost and improve SQL-Statements by JoeALLb
  29. #
  30. ###########################################################################################################
  31. #
  32. # create additional indexes due to performance purposes:
  33. #
  34. # ALTER TABLE 'fhem'.'history' ADD INDEX `Reading_Time_Idx` (`READING`, `TIMESTAMP`) USING BTREE;
  35. #
  36. # Definition: define <name> DbRep <DbLog-Device>
  37. #
  38. # This module uses credentials of DbLog-devices
  39. #
  40. ###########################################################################################################
  41. # Versions History:
  42. #
  43. # 4.10.3 01.02.2017 rename reading "diff-overrun_limit-" to "diff_overrun_limit_",
  44. # collaggstr day aggregation changed back from 4.7.5 change
  45. # 4.10.2 16.01.2017 bugfix uninitialized value $renmode if RenameAgent
  46. # 4.10.1 30.11.2016 bugfix importFromFile format problem if UNIT-field wasn't set
  47. # 4.10 28.12.2016 del_DoParse changed to use Wildcards, del_ParseDone changed to use readingNameMap
  48. # 4.9 23.12.2016 function readingRename added
  49. # 4.8.6 17.12.2016 new bugfix group by-clause due to incompatible changes made in MyQL 5.7.5
  50. # (Forum #msg541103)
  51. # 4.8.5 16.12.2016 bugfix group by-clause due to Forum #msg540610
  52. # 4.8.4 13.12.2016 added "group by ...,table_schema" to select in dbmeta_DoParse due to Forum #msg539228,
  53. # commandref adapted, changed "not_enough_data_in_period" to "less_data_in_period"
  54. # 4.8.3 12.12.2016 balance diff to next period if value of period is 0 between two periods with
  55. # values
  56. # 4.8.2 10.12.2016 bugfix negativ diff if balanced
  57. # 4.8.1 10.12.2016 added balance diff to diffValue, a difference between the last value of an
  58. # old aggregation period to the first value of a new aggregation period will be take over now
  59. # 4.8 09.12.2016 diffValue selection chenged to "between"
  60. # 4.7.7 08.12.2016 code review
  61. # 4.7.6 07.12.2016 DbRep version as internal, check if perl module DBI is installed
  62. # 4.7.5 05.12.2016 collaggstr day aggregation changed
  63. # 4.7.4 28.11.2016 sub calcount changed due to Forum #msg529312
  64. # 4.7.3 20.11.2016 new diffValue function made suitable to SQLite
  65. # 4.7.2 20.11.2016 commandref adapted, state = Warnings adapted
  66. # 4.7.1 17.11.2016 changed fieldlength to DbLog new standard, diffValue state Warnings due to
  67. # several situations and generate readings not_enough_data_in_period, diff-overrun_limit
  68. # 4.7 16.11.2016 sub diffValue changed due to Forum #msg520154, attr diffAccept added,
  69. # diffValue now able to calculate if counter was going to 0
  70. # 4.6.1 01.11.2016 daylight saving time check improved
  71. # 4.6 31.10.2016 bugfix calc issue due to daylight saving time end (winter time)
  72. # 4.5.1 18.10.2016 get svrinfo contains SQLite database file size (MB),
  73. # modified timeout routine
  74. # 4.5 17.10.2016 get data of dbstatus, dbvars, tableinfo, svrinfo (database dependend)
  75. # 4.4 13.10.2016 get function prepared
  76. # 4.3 11.10.2016 Preparation of get metadata
  77. # 4.2 10.10.2016 allow SQL-Wildcards (% _) in attr reading & attr device
  78. # 4.1.3 09.10.2016 bugfix delEntries running on SQLite
  79. # 4.1.2 08.10.2016 old device in DEF of connected DbLog device will substitute by renamed device if
  80. # it is present in DEF
  81. # 4.1.1 06.10.2016 NotifyFn is getting events from global AND own device, set is reduced if
  82. # ROLE=Agent, english commandref enhanced
  83. # 4.1 05.10.2016 DbRep_Attr changed
  84. # 4.0 04.10.2016 Internal/Attribute ROLE added, sub DbRep_firstconnect changed
  85. # NotifyFN activated to start deviceRename if ROLE=Agent
  86. # 3.13 03.10.2016 added deviceRename to rename devices in database, new Internal DATABASE
  87. # 3.12 02.10.2016 function minValue added
  88. # 3.11.1 30.09.2016 bugfix include first and next day in calculation if Timestamp is exactly 'YYYY-MM-DD 00:00:00'
  89. # 3.11 29.09.2016 maxValue calculation moved to background to reduce FHEM-load
  90. # 3.10.1 28.09.2016 sub impFile -> changed $dbh->{AutoCommit} = 0 to $dbh->begin_work
  91. # 3.10 27.09.2016 diffValue calculation moved to background to reduce FHEM-load,
  92. # new reading background_processing_time
  93. # 3.9.1 27.09.2016 Internal "LASTCMD" added
  94. # 3.9 26.09.2016 new function importFromFile to import data from file (CSV format)
  95. # 3.8 16.09.2016 new attr readingPreventFromDel to prevent readings from deletion
  96. # when a new operation starts
  97. # 3.7.3 11.09.2016 changed format of diffValue-reading if no value was selected
  98. # 3.7.2 04.09.2016 problem in diffValue fixed if if no value was selected
  99. # 3.7.1 31.08.2016 Reading "errortext" added, commandref continued, exportToFile changed,
  100. # diffValue changed to fix wrong timestamp if error occur
  101. # 3.7 30.08.2016 exportToFile added (exports data to file (CSV format)
  102. # 3.6 29.08.2016 plausibility checks of database column character length
  103. # 3.5.2 21.08.2016 fit to new commandref style
  104. # 3.5.1 20.08.2016 commandref continued
  105. # 3.5 18.08.2016 new attribute timeOlderThan
  106. # 3.4.4 12.08.2016 current_year_begin, previous_year_begin, current_year_end, previous_year_end
  107. # added as possible values for timestmp attribute
  108. # 3.4.3 09.08.2016 fields for input using "insert" changed to "date,time,value,unit". Attributes
  109. # device, reading will be used to complete dataset,
  110. # now more informations available about faulty datasets in arithmetic operations
  111. # 3.4.2 05.08.2016 commandref complemented, fieldlength used in function "insert" trimmed to 32
  112. # 3.4.1 04.08.2016 check of numeric value type in functions maxvalue, diffvalue
  113. # 3.4 03.08.2016 function "insert" added
  114. # 3.3.3 16.07.2016 bugfix of aggregation=week if month start is 01 and month end is 12 AND
  115. # the last week of december is "01" like in 2014 (checked in version 11804)
  116. # 3.3.2 16.07.2016 readings completed with begin of selection range to ensure valid reading order,
  117. # also done if readingNameMap is set
  118. # 3.3.1 15.07.2016 function "diffValue" changed, write "-" if no value
  119. # 3.3 12.07.2016 function "diffValue" added
  120. # 3.2.1 12.07.2016 DbRep_Notify prepared, switched from readingsSingleUpdate to readingsBulkUpdate
  121. # 3.2 11.07.2016 handling of db-errors is relocated to blockingcall-subs (checked in version 11785)
  122. # 3.1.1 10.07.2016 state turns to initialized and connected after attr "disabled" is switched from "1" to "0"
  123. # 3.1 09.07.2016 new Attr "timeDiffToNow" and change subs according to that
  124. # 3.0 04.07.2016 no selection if timestamp isn't set and aggregation isn't set with fetchrows, delEntries
  125. # 2.9.9 03.07.2016 english version of commandref completed
  126. # 2.9.8 01.07.2016 changed fetchrows_ParseDone to handle readingvalues with whitespaces correctly
  127. # 2.9.7 30.06.2016 moved {DBLOGDEVICE} to {HELPER}{DBLOGDEVICE}
  128. # 2.9.6 30.06.2016 sql-call changed for countEntries, averageValue, sumValue avoiding
  129. # problems if no timestamp is set and aggregation is set
  130. # 2.9.5 30.06.2016 format of readingnames changed again (substitute ":" with "-" in time)
  131. # 2.9.4 30.06.2016 change readingmap to readingNameMap, prove of unsupported characters added
  132. # 2.9.3 27.06.2016 format of readingnames changed avoiding some problems after restart and splitting
  133. # 2.9.2 27.06.2016 use Time::Local added, DbRep_firstconnect added
  134. # 2.9.1 26.06.2016 german commandref added
  135. # 2.9 25.06.2016 attributes showproctime, timeout added
  136. # 2.8.1 24.06.2016 sql-creation of sumValue, maxValue, fetchrows changed
  137. # main-routine changed
  138. # 2.8 24.06.2016 function averageValue changed to nonblocking function
  139. # 2.7.1 24.06.2016 changed blockingcall routines, changed to unique abort-function
  140. # 2.7 23.06.2016 changed function countEntries to nonblocking
  141. # 2.6.3 22.06.2016 abort-routines changed, dbconnect-routines changed
  142. # 2.6.2 21.06.2016 aggregation week corrected
  143. # 2.6.1 20.06.2016 routine maxval_ParseDone corrected
  144. # 2.6 31.05.2016 maxValue changed to nonblocking function
  145. # 2.5.3 31.05.2016 function delEntries changed
  146. # 2.5.2 31.05.2016 ping check changed, DbRep_Connect changed
  147. # 2.5.1 30.05.2016 sleep in nb-functions deleted
  148. # 2.5 30.05.2016 changed to use own $dbh with DbLog-credentials, function sumValue, fetchrows
  149. # 2.4.2 29.05.2016 function sumValue changed
  150. # 2.4.1 29.05.2016 function fetchrow changed
  151. # 2.4 29.05.2016 changed to nonblocking function for sumValue
  152. # 2.3 28.05.2016 changed sumValue to "prepare" with placeholders
  153. # 2.2 27.05.2016 changed fetchrow and delEntries function to "prepare" with placeholders
  154. # added nonblocking function for delEntries
  155. # 2.1 25.05.2016 codechange
  156. # 2.0 24.05.2016 added nonblocking function for fetchrow
  157. # 1.2 21.05.2016 function and attribute for delEntries added
  158. # 1.1 20.05.2016 change result-format of "count", move runtime-counter to sub collaggstr
  159. # 1.0 19.05.2016 Initial
  160. #
  161. package main;
  162. use strict;
  163. use warnings;
  164. use POSIX qw(strftime);
  165. use Time::HiRes qw(gettimeofday tv_interval);
  166. use Scalar::Util qw(looks_like_number);
  167. eval "use DBI;1" or my $DbRepMMDBI = "DBI";
  168. use DBI::Const::GetInfoType;
  169. use Blocking;
  170. use Time::Local;
  171. # no if $] >= 5.017011, warnings => 'experimental';
  172. my $DbRepVersion = "4.10.3";
  173. my %dbrep_col = ("DEVICE" => 64,
  174. "TYPE" => 64,
  175. "EVENT" => 512,
  176. "READING" => 64,
  177. "VALUE" => 128,
  178. "UNIT" => 32
  179. );
  180. ###################################################################################
  181. # DbRep_Initialize
  182. ###################################################################################
  183. sub DbRep_Initialize($) {
  184. my ($hash) = @_;
  185. $hash->{DefFn} = "DbRep_Define";
  186. $hash->{UndefFn} = "DbRep_Undef";
  187. $hash->{NotifyFn} = "DbRep_Notify";
  188. $hash->{SetFn} = "DbRep_Set";
  189. $hash->{GetFn} = "DbRep_Get";
  190. $hash->{AttrFn} = "DbRep_Attr";
  191. $hash->{AttrList} = "disable:1,0 ".
  192. "reading ".
  193. "allowDeletion:1,0 ".
  194. "readingNameMap ".
  195. "readingPreventFromDel ".
  196. "device ".
  197. "expimpfile ".
  198. "aggregation:hour,day,week,month,no ".
  199. "diffAccept ".
  200. "role:Client,Agent ".
  201. "showproctime:1,0 ".
  202. "showSvrInfo ".
  203. "showVariables ".
  204. "showStatus ".
  205. "showTableInfo ".
  206. "timestamp_begin ".
  207. "timestamp_end ".
  208. "timeDiffToNow ".
  209. "timeOlderThan ".
  210. "timeout ".
  211. $readingFnAttributes;
  212. return undef;
  213. }
  214. ###################################################################################
  215. # DbRep_Define
  216. ###################################################################################
  217. sub DbRep_Define($@) {
  218. # define <name> DbRep <DbLog-Device>
  219. # ($hash) [1] [2]
  220. #
  221. my ($hash, $def) = @_;
  222. my $name = $hash->{NAME};
  223. return "Error: Perl module ".$DbRepMMDBI." is missing.
  224. Install it on Debian with: sudo apt-get install libdbi-perl" if($DbRepMMDBI);
  225. my @a = split("[ \t][ \t]*", $def);
  226. if(int(@a) < 2) {
  227. return "You need to specify more parameters.\n". "Format: define <name> DbRep <DbLog-Device> <Reading> <Timestamp-Begin> <Timestamp-Ende>";
  228. }
  229. $hash->{LASTCMD} = " ";
  230. $hash->{ROLE} = AttrVal($name, "role", "Client");
  231. $hash->{HELPER}{DBLOGDEVICE} = $a[2];
  232. $hash->{VERSION} = $DbRepVersion;
  233. $hash->{NOTIFYDEV} = "global,".$name; # nur Events dieser Devices an DbRep_Notify weiterleiten
  234. my $dbconn = $defs{$a[2]}{dbconn};
  235. $hash->{DATABASE} = (split(/;|=/, $dbconn))[1];
  236. RemoveInternalTimer($hash);
  237. InternalTimer(time+5, 'DbRep_firstconnect', $hash, 0);
  238. Log3 ($name, 4, "DbRep $name - initialized");
  239. readingsSingleUpdate($hash, 'state', 'initialized', 1);
  240. return undef;
  241. }
  242. ###################################################################################
  243. # DbRep_Set
  244. ###################################################################################
  245. sub DbRep_Set($@) {
  246. my ($hash, @a) = @_;
  247. return "\"set X\" needs at least an argument" if ( @a < 2 );
  248. my $name = $a[0];
  249. my $opt = $a[1];
  250. my $prop = $a[2];
  251. my $dbh = $hash->{DBH};
  252. my $dblogdevice = $hash->{HELPER}{DBLOGDEVICE};
  253. $hash->{dbloghash} = $defs{$dblogdevice};
  254. my $dbmodel = $hash->{dbloghash}{DBMODEL};
  255. my $setlist = "Unknown argument $opt, choose one of ".
  256. (($hash->{ROLE} ne "Agent")?"sumValue:noArg ":"").
  257. (($hash->{ROLE} ne "Agent")?"averageValue:noArg ":"").
  258. (($hash->{ROLE} ne "Agent")?"delEntries:noArg ":"").
  259. "deviceRename ".
  260. (($hash->{ROLE} ne "Agent")?"readingRename ":"").
  261. (($hash->{ROLE} ne "Agent")?"exportToFile:noArg ":"").
  262. (($hash->{ROLE} ne "Agent")?"importFromFile:noArg ":"").
  263. (($hash->{ROLE} ne "Agent")?"maxValue:noArg ":"").
  264. (($hash->{ROLE} ne "Agent")?"minValue:noArg ":"").
  265. (($hash->{ROLE} ne "Agent")?"fetchrows:noArg ":"").
  266. (($hash->{ROLE} ne "Agent")?"diffValue:noArg ":"").
  267. (($hash->{ROLE} ne "Agent")?"insert ":"").
  268. (($hash->{ROLE} ne "Agent")?"countEntries:noArg ":"");
  269. return if(IsDisabled($name));
  270. if ($opt eq "countEntries" && $hash->{ROLE} ne "Agent") {
  271. sqlexec($hash,$opt);
  272. } elsif ($opt eq "fetchrows" && $hash->{ROLE} ne "Agent") {
  273. sqlexec($hash,$opt);
  274. } elsif ($opt =~ m/(max|min|sum|average|diff)Value/ && $hash->{ROLE} ne "Agent") {
  275. if (!AttrVal($hash->{NAME}, "reading", "")) {
  276. return " The attribute reading to analyze is not set !";
  277. }
  278. sqlexec($hash,$opt);
  279. } elsif ($opt eq "delEntries" && $hash->{ROLE} ne "Agent") {
  280. if (!AttrVal($hash->{NAME}, "allowDeletion", undef)) {
  281. return " Set attribute 'allowDeletion' if you want to allow deletion of any database entries. Use it with care !";
  282. }
  283. sqlexec($hash,$opt);
  284. } elsif ($opt eq "deviceRename") {
  285. my ($olddev, $newdev) = split(",",$prop);
  286. if (!$olddev || !$newdev) {return "Both entries \"old device name\", \"new device name\" are needed. Use \"set ... deviceRename olddevname,newdevname\" ";}
  287. $hash->{HELPER}{OLDDEV} = $olddev;
  288. $hash->{HELPER}{NEWDEV} = $newdev;
  289. $hash->{HELPER}{RENMODE} = "devren";
  290. sqlexec($hash,$opt);
  291. } elsif ($opt eq "readingRename") {
  292. my ($oldread, $newread) = split(",",$prop);
  293. if (!$oldread || !$newread) {return "Both entries \"old reading name\", \"new reading name\" are needed. Use \"set ... readingRename oldreadingname,newreadingname\" ";}
  294. $hash->{HELPER}{OLDREAD} = $oldread;
  295. $hash->{HELPER}{NEWREAD} = $newread;
  296. $hash->{HELPER}{RENMODE} = "readren";
  297. sqlexec($hash,$opt);
  298. } elsif ($opt eq "insert" && $hash->{ROLE} ne "Agent") {
  299. if ($prop) {
  300. if (!AttrVal($hash->{NAME}, "device", "") || !AttrVal($hash->{NAME}, "reading", "") ) {
  301. return "One or both of attributes \"device\", \"reading\" is not set. It's mandatory to set both to complete dataset for manual insert !";
  302. }
  303. # Attribute device & reading dürfen kein SQL-Wildcard % enthalten
  304. return "One or both of attributes \"device\", \"reading\" containing SQL wildcard \"%\". Wildcards are not allowed in function manual insert !"
  305. if(AttrVal($hash->{NAME},"device","") =~ m/%/ || AttrVal($hash->{NAME},"reading","") =~ m/%/ );
  306. my ($i_date, $i_time, $i_value, $i_unit) = split(",",$prop);
  307. if (!$i_date || !$i_time || !$i_value) {return "At least data for \"Date\", \"Time\" and \"Value\" is needed to insert. \"Unit\" is optional. Inputformat is 'YYYY-MM-DD,HH:MM:SS,<Value(32)>,<Unit(32)>' ";}
  308. unless ($i_date =~ /(\d{4})-(\d{2})-(\d{2})/) {return "Input for date is not valid. Use format YYYY-MM-DD !";}
  309. unless ($i_time =~ /(\d{2}):(\d{2}):(\d{2})/) {return "Input for time is not valid. Use format HH:MM:SS !";}
  310. my $i_timestamp = $i_date." ".$i_time;
  311. my ($yyyy, $mm, $dd, $hh, $min, $sec) = ($i_timestamp =~ /(\d+)-(\d+)-(\d+) (\d+):(\d+):(\d+)/);
  312. eval { my $ts = timelocal($sec, $min, $hh, $dd, $mm-1, $yyyy-1900); };
  313. if ($@) {
  314. my @l = split (/at/, $@);
  315. return " Timestamp is out of range - $l[0]";
  316. }
  317. my $i_device = AttrVal($hash->{NAME}, "device", "");
  318. my $i_reading = AttrVal($hash->{NAME}, "reading", "");
  319. # Daten auf maximale Länge (entsprechend der Feldlänge in DbLog DB create-scripts) beschneiden wenn nicht SQLite
  320. if ($dbmodel ne 'SQLITE') {
  321. $i_device = substr($i_device,0, $dbrep_col{DEVICE});
  322. $i_reading = substr($i_reading,0, $dbrep_col{READING});
  323. $i_value = substr($i_value,0, $dbrep_col{VALUE});
  324. $i_unit = substr($i_unit,0, $dbrep_col{UNIT}) if($i_unit);
  325. }
  326. $hash->{HELPER}{I_TIMESTAMP} = $i_timestamp;
  327. $hash->{HELPER}{I_DEVICE} = $i_device;
  328. $hash->{HELPER}{I_READING} = $i_reading;
  329. $hash->{HELPER}{I_VALUE} = $i_value;
  330. $hash->{HELPER}{I_UNIT} = $i_unit;
  331. $hash->{HELPER}{I_TYPE} = my $i_type = "manual";
  332. $hash->{HELPER}{I_EVENT} = my $i_event = "manual";
  333. } else {
  334. return "Data to insert to table 'history' are needed like this pattern: 'Date,Time,Value,[Unit]'. \"Unit\" is optional. Spaces are not allowed !";
  335. }
  336. sqlexec($hash,$opt);
  337. } elsif ($opt eq "exportToFile" && $hash->{ROLE} ne "Agent") {
  338. if (!AttrVal($hash->{NAME}, "expimpfile", "")) {
  339. return "The attribute \"expimpfile\" (path and filename) has to be set for export to file !";
  340. }
  341. sqlexec($hash,$opt);
  342. } elsif ($opt eq "importFromFile" && $hash->{ROLE} ne "Agent") {
  343. if (!AttrVal($hash->{NAME}, "expimpfile", "")) {
  344. return "The attribute \"expimpfile\" (path and filename) has to be set for import from file !";
  345. }
  346. sqlexec($hash,$opt);
  347. }
  348. else
  349. {
  350. return "$setlist";
  351. }
  352. $hash->{LASTCMD} = "$opt";
  353. return undef;
  354. }
  355. ###################################################################################
  356. # DbRep_Get
  357. ###################################################################################
  358. sub DbRep_Get($@) {
  359. my ($hash, @a) = @_;
  360. return "\"get X\" needs at least an argument" if ( @a < 2 );
  361. my $name = $a[0];
  362. my $opt = $a[1];
  363. my $prop = $a[2];
  364. my $dbh = $hash->{DBH};
  365. my $dblogdevice = $hash->{HELPER}{DBLOGDEVICE};
  366. $hash->{dbloghash} = $defs{$dblogdevice};
  367. my $dbmodel = $hash->{dbloghash}{DBMODEL};
  368. my $to = AttrVal($name, "timeout", "60");
  369. my $getlist = "Unknown argument $opt, choose one of ".
  370. "svrinfo:noArg ".
  371. (($dbmodel eq "MYSQL")?"dbstatus:noArg ":"").
  372. (($dbmodel eq "MYSQL")?"tableinfo:noArg ":"").
  373. (($dbmodel eq "MYSQL")?"dbvars:noArg ":"")
  374. ;
  375. return if(IsDisabled($name));
  376. if ($opt eq "dbvars" || $opt eq "dbstatus" || $opt eq "tableinfo") {
  377. return "The operation \"$opt\" isn't available with database type $dbmodel" if ($dbmodel ne 'MYSQL');
  378. readingsSingleUpdate($hash, "state", "running", 1);
  379. delread($hash); # Readings löschen die nicht in der Ausnahmeliste (Attr readingPreventFromDel) stehen
  380. $hash->{HELPER}{RUNNING_PID} = BlockingCall("dbmeta_DoParse", "$name|$opt", "dbmeta_ParseDone", $to, "ParseAborted", $hash);
  381. } elsif ($opt eq "svrinfo") {
  382. delread($hash);
  383. readingsSingleUpdate($hash, "state", "running", 1);
  384. $hash->{HELPER}{RUNNING_PID} = BlockingCall("dbmeta_DoParse", "$name|$opt", "dbmeta_ParseDone", $to, "ParseAborted", $hash);
  385. }
  386. else
  387. {
  388. return "$getlist";
  389. }
  390. $hash->{LASTCMD} = "$opt";
  391. return undef;
  392. }
  393. ###################################################################################
  394. # DbRep_Attr
  395. ###################################################################################
  396. sub DbRep_Attr($$$$) {
  397. my ($cmd,$name,$aName,$aVal) = @_;
  398. my $hash = $defs{$name};
  399. $hash->{dbloghash} = $defs{$hash->{HELPER}{DBLOGDEVICE}};
  400. my $dbmodel = $hash->{dbloghash}{DBMODEL};
  401. my $do;
  402. # $cmd can be "del" or "set"
  403. # $name is device name
  404. # aName and aVal are Attribute name and value
  405. # nicht erlaubte / zu löschende Attribute wenn role = Agent
  406. my @agentnoattr = qw(aggregation
  407. allowDeletion
  408. reading
  409. readingNameMap
  410. readingPreventFromDel
  411. device
  412. diffAccept
  413. expimpfile
  414. timestamp_begin
  415. timestamp_end
  416. timeDiffToNow
  417. timeOlderThan
  418. );
  419. if ($aName eq "disable") {
  420. if($cmd eq "set") {
  421. $do = ($aVal) ? 1 : 0;
  422. }
  423. $do = 0 if($cmd eq "del");
  424. my $val = ($do == 1 ? "disabled" : "initialized");
  425. readingsSingleUpdate($hash, "state", $val, 1);
  426. if ($do == 0) {
  427. RemoveInternalTimer($hash);
  428. InternalTimer(time+5, 'DbRep_firstconnect', $hash, 0);
  429. } else {
  430. my $dbh = $hash->{DBH};
  431. $dbh->disconnect() if($dbh);
  432. }
  433. }
  434. if ($cmd eq "set" && $hash->{ROLE} eq "Agent") {
  435. foreach (@agentnoattr) {
  436. return ("Attribute $aName is not usable due to role of $name is \"$hash->{ROLE}\" ") if ($_ eq $aName);
  437. }
  438. }
  439. if ($aName eq "readingPreventFromDel") {
  440. if($cmd eq "set") {
  441. if($aVal =~ / /) {return "Usage of $aName is wrong. Use a comma separated list of readings which are should prevent from deletion when a new selection starts.";}
  442. $hash->{HELPER}{RDPFDEL} = $aVal;
  443. } else {
  444. delete $hash->{HELPER}{RDPFDEL} if($hash->{HELPER}{RDPFDEL});
  445. }
  446. }
  447. if ($aName eq "role") {
  448. if($cmd eq "set") {
  449. if ($aVal eq "Agent") {
  450. # check ob bereits ein Agent für die angeschlossene Datenbank existiert -> DbRep-Device kann dann keine Agent-Rolle einnehmen
  451. foreach(devspec2array("TYPE=DbRep")) {
  452. my $devname = $_;
  453. next if($devname eq $name);
  454. my $devrole = $defs{$_}{ROLE};
  455. my $devdb = $defs{$_}{DATABASE};
  456. if ($devrole eq "Agent" && $devdb eq $hash->{DATABASE}) { return "There is already an Agent device: $devname defined for database $hash->{DATABASE} !"; }
  457. }
  458. # nicht erlaubte Attribute löschen falls gesetzt
  459. foreach (@agentnoattr) {
  460. delete($attr{$name}{$_});
  461. }
  462. $attr{$name}{icon} = "security";
  463. }
  464. $do = $aVal;
  465. } else {
  466. $do = "Client";
  467. }
  468. $hash->{ROLE} = $do;
  469. delete($attr{$name}{icon}) if($do eq "Client");
  470. }
  471. if ($cmd eq "set") {
  472. if ($aName eq "timestamp_begin" || $aName eq "timestamp_end") {
  473. if ($aVal eq "current_year_begin" || $aVal eq "previous_year_begin" || $aVal eq "current_year_end" || $aVal eq "previous_year_end") {
  474. delete($attr{$name}{timeDiffToNow}) if ($attr{$name}{timeDiffToNow});
  475. delete($attr{$name}{timeOlderThan}) if ($attr{$name}{timeOlderThan});
  476. return undef;
  477. }
  478. unless ($aVal =~ /(19[0-9][0-9]|2[0-9][0-9][0-9])-(0[1-9]|1[1-2])-(0[1-9]|1[0-9]|2[0-9]|3[0-1]) (0[0-9])|1[1-9]|2[0-3]:([0-5][0-9]):([0-5][0-9])/)
  479. {return " The Value for $aName is not valid. Use format YYYY-MM-DD HH:MM:SS or one of \"current_year_begin\",\"current_year_end\", \"previous_year_begin\", \"previous_year_end\" !";}
  480. my ($yyyy, $mm, $dd, $hh, $min, $sec) = ($aVal =~ /(\d+)-(\d+)-(\d+) (\d+):(\d+):(\d+)/);
  481. eval { my $epoch_seconds_begin = timelocal($sec, $min, $hh, $dd, $mm-1, $yyyy-1900); };
  482. if ($@) {
  483. my @l = split (/at/, $@);
  484. return " The Value of $aName is out of range - $l[0]";
  485. }
  486. delete($attr{$name}{timeDiffToNow}) if ($attr{$name}{timeDiffToNow});
  487. delete($attr{$name}{timeOlderThan}) if ($attr{$name}{timeOlderThan});
  488. }
  489. if ($aName eq "timeout" || $aName eq "diffAccept") {
  490. unless ($aVal =~ /^[0-9]+$/) { return " The Value of $aName is not valid. Use only figures 0-9 without decimal places !";}
  491. }
  492. if ($aName eq "readingNameMap") {
  493. unless ($aVal =~ m/^[A-Za-z\d_\.-]+$/) { return " Unsupported character in $aName found. Use only A-Z a-z _ . -";}
  494. }
  495. if ($aName eq "timeDiffToNow") {
  496. unless ($aVal =~ /^[0-9]+$/) { return "The Value of $aName is not valid. Use only figures 0-9 without decimal places. It's the time (in seconds) before current time used as start of selection. Refer to commandref !";}
  497. delete($attr{$name}{timestamp_begin}) if ($attr{$name}{timestamp_begin});
  498. delete($attr{$name}{timestamp_end}) if ($attr{$name}{timestamp_end});
  499. delete($attr{$name}{timeOlderThan}) if ($attr{$name}{timeOlderThan});
  500. }
  501. if ($aName eq "timeOlderThan") {
  502. unless ($aVal =~ /^[0-9]+$/) { return "The Value of $aName is not valid. Use only figures 0-9 without decimal places. It's the time (in seconds) before current time used as end of selection. Refer to commandref !";}
  503. delete($attr{$name}{timestamp_begin}) if ($attr{$name}{timestamp_begin});
  504. delete($attr{$name}{timestamp_end}) if ($attr{$name}{timestamp_end});
  505. delete($attr{$name}{timeDiffToNow}) if ($attr{$name}{timeDiffToNow});
  506. }
  507. if ($aName eq "reading" || $aName eq "device") {
  508. if ($dbmodel && $dbmodel ne 'SQLITE') {
  509. if ($dbmodel eq 'POSTGRESQL') {
  510. return "Length of \"$aName\" is too big. Maximum length for database type $dbmodel is $dbrep_col{READING}" if(length($aVal) > $dbrep_col{READING});
  511. } elsif ($dbmodel eq 'MYSQL') {
  512. return "Length of \"$aName\" is too big. Maximum length for database type $dbmodel is $dbrep_col{READING}" if(length($aVal) > $dbrep_col{READING});
  513. }
  514. }
  515. }
  516. }
  517. return undef;
  518. }
  519. ###################################################################################
  520. # DbRep_Notify Eventverarbeitung
  521. ###################################################################################
  522. sub DbRep_Notify($$) {
  523. # Es werden nur die Events von Geräten verarbeitet die im Hash $hash->{NOTIFYDEV} gelistet sind (wenn definiert).
  524. # Dadurch kann die Menge der Events verringert werden. In sub DbRep_Define angeben.
  525. # Beispiele:
  526. # $hash->{NOTIFYDEV} = "global";
  527. # $hash->{NOTIFYDEV} = "global,Definition_A,Definition_B";
  528. my ($own_hash, $dev_hash) = @_;
  529. my $myName = $own_hash->{NAME}; # Name des eigenen Devices
  530. my $devName = $dev_hash->{NAME}; # Device welches Events erzeugt hat
  531. return if(IsDisabled($myName)); # Return if the module is disabled
  532. my $events = deviceEvents($dev_hash,0);
  533. return if(!$events);
  534. foreach my $event (@{$events}) {
  535. $event = "" if(!defined($event));
  536. my @evl = split("[ \t][ \t]*", $event);
  537. # if ($devName = $myName && $evl[0] =~ /done/) {
  538. # InternalTimer(time+1, "browser_refresh", $own_hash, 0);
  539. # }
  540. # wenn Rolle "Agent" Verbeitung von RENAMED Events
  541. if ($own_hash->{ROLE} eq "Agent") {
  542. next if ($event !~ /RENAMED/);
  543. my $strucChanged;
  544. # altes in neues device in der DEF des angeschlossenen DbLog-device ändern (neues device loggen)
  545. my $dblog_name = $own_hash->{dbloghash}{NAME}; # Name des an den DbRep-Agenten angeschlossenen DbLog-Dev
  546. my $dblog_hash = $defs{$dblog_name};
  547. if ( $dblog_hash->{DEF} =~ m/( |\(|\|)$evl[1]( |\)|\||:)/ ) {
  548. $dblog_hash->{DEF} =~ s/$evl[1]/$evl[2]/;
  549. $dblog_hash->{REGEXP} =~ s/$evl[1]/$evl[2]/;
  550. # Definitionsänderung wurde vorgenommen
  551. $strucChanged = 1;
  552. Log3 ($myName, 3, "DbRep Agent $myName - $dblog_name substituted in DEF, old: \"$evl[1]\", new: \"$evl[2]\" ");
  553. }
  554. # DEVICE innerhalb angeschlossener Datenbank umbenennen
  555. Log3 ($myName, 4, "DbRep Agent $myName - Evt RENAMED rec - old device: $evl[1], new device: $evl[2] -> start deviceRename in DB: $own_hash->{DATABASE} ");
  556. $own_hash->{HELPER}{OLDDEV} = $evl[1];
  557. $own_hash->{HELPER}{NEWDEV} = $evl[2];
  558. $own_hash->{HELPER}{RENMODE} = "devren";
  559. sqlexec($own_hash,"deviceRename");
  560. # die Attribute "device" in allen DbRep-Devices mit der Datenbank = DB des Agenten von alten Device in neues Device ändern
  561. foreach(devspec2array("TYPE=DbRep")) {
  562. my $repname = $_;
  563. next if($_ eq $myName);
  564. my $repattrdevice = $attr{$_}{device};
  565. next if(!$repattrdevice);
  566. my $repdb = $defs{$_}{DATABASE};
  567. if ($repattrdevice eq $evl[1] && $repdb eq $own_hash->{DATABASE}) {
  568. $attr{$_}{device} = $evl[2];
  569. # Definitionsänderung wurde vorgenommen
  570. $strucChanged = 1;
  571. Log3 ($myName, 3, "DbRep Agent $myName - $_ attr device changed, old: \"$evl[1]\", new: \"$evl[2]\" ");
  572. }
  573. }
  574. # if ($strucChanged) {CommandSave("","")};
  575. }
  576. }
  577. }
  578. ###################################################################################
  579. # DbRep_Undef
  580. ###################################################################################
  581. sub DbRep_Undef($$) {
  582. my ($hash, $arg) = @_;
  583. RemoveInternalTimer($hash);
  584. my $dbh = $hash->{DBH};
  585. $dbh->disconnect() if(defined($dbh));
  586. BlockingKill($hash->{HELPER}{RUNNING_PID}) if (exists($hash->{HELPER}{RUNNING_PID}));
  587. return undef;
  588. }
  589. ###################################################################################
  590. # First Init DB Connect
  591. ###################################################################################
  592. sub DbRep_firstconnect($) {
  593. my ($hash)= @_;
  594. my $name = $hash->{NAME};
  595. my $dblogdevice = $hash->{HELPER}{DBLOGDEVICE};
  596. $hash->{dbloghash} = $defs{$dblogdevice};
  597. my $dbconn = $hash->{dbloghash}{dbconn};
  598. if ($init_done == 1) {
  599. if ( !DbRep_Connect($hash) ) {
  600. Log3 ($name, 2, "DbRep $name - DB connect failed. Credentials of database $hash->{DATABASE} are valid and database reachable ?");
  601. readingsSingleUpdate($hash, "state", "disconnected", 1);
  602. } else {
  603. Log3 ($name, 4, "DbRep $name - Connectiontest to db $dbconn successful");
  604. my $dbh = $hash->{DBH};
  605. $dbh->disconnect();
  606. }
  607. } else {
  608. RemoveInternalTimer($hash, "DbRep_firstconnect");
  609. InternalTimer(time+1, "DbRep_firstconnect", $hash, 0);
  610. }
  611. return;
  612. }
  613. ###################################################################################
  614. # DB Connect
  615. ###################################################################################
  616. sub DbRep_Connect($) {
  617. my ($hash)= @_;
  618. my $name = $hash->{NAME};
  619. my $dbloghash = $hash->{dbloghash};
  620. my $dbconn = $dbloghash->{dbconn};
  621. my $dbuser = $dbloghash->{dbuser};
  622. my $dblogname = $dbloghash->{NAME};
  623. my $dbpassword = $attr{"sec$dblogname"}{secret};
  624. my $dbh;
  625. eval {$dbh = DBI->connect("dbi:$dbconn", $dbuser, $dbpassword, { PrintError => 0, RaiseError => 1, AutoCommit => 1 });};
  626. if(!$dbh) {
  627. RemoveInternalTimer($hash);
  628. Log3 ($name, 3, "DbRep $name - Connectiontest to database $dbconn with user $dbuser");
  629. readingsSingleUpdate($hash, 'state', 'disconnected', 1);
  630. InternalTimer(time+5, 'DbRep_Connect', $hash, 0);
  631. Log3 ($name, 3, "DbRep $name - Waiting for database connection");
  632. return 0;
  633. }
  634. $hash->{DBH} = $dbh;
  635. readingsSingleUpdate($hash, "state", "connected", 1);
  636. Log3 ($name, 3, "DbRep $name - connected");
  637. return 1;
  638. }
  639. ################################################################################################################
  640. # Hauptroutine "Set"
  641. ################################################################################################################
  642. sub sqlexec($$) {
  643. my ($hash,$opt) = @_;
  644. my $name = $hash->{NAME};
  645. my $to = AttrVal($name, "timeout", "60");
  646. my $reading = AttrVal($name, "reading", undef);
  647. my $aggregation = AttrVal($name, "aggregation", "no"); # wichtig !! aggregation niemals "undef"
  648. my $device = AttrVal($name, "device", undef);
  649. my $aggsec;
  650. # Entkommentieren für Testroutine im Vordergrund
  651. # testexit($hash);
  652. if (exists($hash->{HELPER}{RUNNING_PID}) && $hash->{ROLE} ne "Agent") {
  653. Log3 ($name, 3, "DbRep $name - WARNING - old process $hash->{HELPER}{RUNNING_PID}{pid} will be killed now to start a new BlockingCall");
  654. BlockingKill($hash->{HELPER}{RUNNING_PID});
  655. }
  656. # Readings löschen die nicht in der Ausnahmeliste (Attr readingPreventFromDel) stehen
  657. delread($hash);
  658. readingsSingleUpdate($hash, "state", "running", 1);
  659. # only for this block because of warnings if details of readings are not set
  660. no warnings 'uninitialized';
  661. # Ausgaben und Zeitmanipulationen
  662. Log3 ($name, 4, "DbRep $name - -------- New selection --------- ");
  663. Log3 ($name, 4, "DbRep $name - Aggregation: $aggregation");
  664. Log3 ($name, 4, "DbRep $name - Command: $opt");
  665. # Auswertungszeit Beginn (String)
  666. # dynamische Berechnung von Startdatum/zeit aus current_year_begin / previous_year_begin
  667. # timestamp in SQL format YYYY-MM-DD hh:mm:ss
  668. my $cy = strftime "%Y", localtime; # aktuelles Jahr
  669. my $tsbegin;
  670. if (AttrVal($hash->{NAME}, "timestamp_begin", "") eq "current_year_begin") {
  671. $tsbegin = $cy."-01-01 00:00:00";
  672. } elsif (AttrVal($hash->{NAME}, "timestamp_begin", "") eq "previous_year_begin") {
  673. $tsbegin = ($cy-1)."-01-01 00:00:00";
  674. } else {
  675. $tsbegin = AttrVal($hash->{NAME}, "timestamp_begin", "1970-01-01 01:00:00");
  676. }
  677. # Auswertungszeit Ende (String)
  678. # dynamische Berechnung von Endedatum/zeit aus current_year_begin / previous_year_begin
  679. # timestamp in SQL format YYYY-MM-DD hh:mm:ss
  680. my $tsend;
  681. if (AttrVal($hash->{NAME}, "timestamp_end", "") eq "current_year_end") {
  682. $tsend = $cy."-12-31 23:59:59";
  683. } elsif (AttrVal($hash->{NAME}, "timestamp_end", "") eq "previous_year_end") {
  684. $tsend = ($cy-1)."-12-31 23:59:59";
  685. } else {
  686. $tsend = AttrVal($hash->{NAME}, "timestamp_end", strftime "%Y-%m-%d %H:%M:%S", localtime(time));
  687. }
  688. # extrahieren der Einzelwerte von Datum/Zeit Beginn
  689. my ($yyyy1, $mm1, $dd1, $hh1, $min1, $sec1) = ($tsbegin =~ /(\d+)-(\d+)-(\d+) (\d+):(\d+):(\d+)/);
  690. # extrahieren der Einzelwerte von Datum/Zeit Ende
  691. my ($yyyy2, $mm2, $dd2, $hh2, $min2, $sec2) = ($tsend =~ /(\d+)-(\d+)-(\d+) (\d+):(\d+):(\d+)/);
  692. # Umwandeln in Epochesekunden Beginn
  693. my $epoch_seconds_begin = timelocal($sec1, $min1, $hh1, $dd1, $mm1-1, $yyyy1-1900) if($tsbegin);
  694. if(AttrVal($hash->{NAME}, "timeDiffToNow", undef)) {
  695. $epoch_seconds_begin = time() - AttrVal($hash->{NAME}, "timeDiffToNow", undef);
  696. Log3 ($name, 4, "DbRep $name - Time difference to current time for calculating Timestamp begin: ".AttrVal($hash->{NAME}, "timeDiffToNow", undef)." sec");
  697. } elsif (AttrVal($hash->{NAME}, "timeOlderThan", undef)) {
  698. $epoch_seconds_begin = timelocal(00, 00, 01, 01, 01-1, 1970-1900);
  699. }
  700. my $tsbegin_string = strftime "%Y-%m-%d %H:%M:%S", localtime($epoch_seconds_begin);
  701. Log3 ($name, 5, "DbRep $name - Timestamp begin epocheseconds: $epoch_seconds_begin");
  702. Log3 ($name, 4, "DbRep $name - Timestamp begin human readable: $tsbegin_string");
  703. # Umwandeln in Epochesekunden Endezeit
  704. my $epoch_seconds_end = timelocal($sec2, $min2, $hh2, $dd2, $mm2-1, $yyyy2-1900);
  705. $epoch_seconds_end = AttrVal($hash->{NAME}, "timeOlderThan", undef) ? (time() - AttrVal($hash->{NAME}, "timeOlderThan", undef)) : $epoch_seconds_end;
  706. Log3 ($name, 4, "DbRep $name - Time difference to current time for calculating Timestamp end: ".AttrVal($hash->{NAME}, "timeOlderThan", undef)." sec") if(AttrVal($hash->{NAME}, "timeOlderThan", undef));
  707. my $tsend_string = strftime "%Y-%m-%d %H:%M:%S", localtime($epoch_seconds_end);
  708. Log3 ($name, 5, "DbRep $name - Timestamp end epocheseconds: $epoch_seconds_end");
  709. Log3 ($name, 4, "DbRep $name - Timestamp end human readable: $tsend_string");
  710. # Erstellung Wertehash für "collaggstr"
  711. my $runtime = $epoch_seconds_begin; # Schleifenlaufzeit auf Beginn der Zeitselektion setzen
  712. my $runtime_string; # Datum/Zeit im SQL-Format für Readingname Teilstring
  713. my $runtime_string_first; # Datum/Zeit Auswertungsbeginn im SQL-Format für SQL-Statement
  714. my $runtime_string_next; # Datum/Zeit + Periode (Granularität) für Auswertungsende im SQL-Format
  715. my $reading_runtime_string; # zusammengesetzter Readingname+Aggregation für Update
  716. my $tsstr = strftime "%H:%M:%S", localtime($runtime); # für Berechnung Tagesverschieber / Stundenverschieber
  717. my $testr = strftime "%H:%M:%S", localtime($epoch_seconds_end); # für Berechnung Tagesverschieber / Stundenverschieber
  718. my $dsstr = strftime "%Y-%m-%d", localtime($runtime); # für Berechnung Tagesverschieber / Stundenverschieber
  719. my $destr = strftime "%Y-%m-%d", localtime($epoch_seconds_end); # für Berechnung Tagesverschieber / Stundenverschieber
  720. my $msstr = strftime "%m", localtime($runtime); # Startmonat für Berechnung Monatsverschieber
  721. my $mestr = strftime "%m", localtime($epoch_seconds_end); # Endemonat für Berechnung Monatsverschieber
  722. my $ysstr = strftime "%Y", localtime($runtime); # Startjahr für Berechnung Monatsverschieber
  723. my $yestr = strftime "%Y", localtime($epoch_seconds_end); # Endejahr für Berechnung Monatsverschieber
  724. my $wd = strftime "%a", localtime($runtime); # Wochentag des aktuellen Startdatum/Zeit
  725. my $wdadd = 604800 if($wd eq "Mo"); # wenn Start am "Mo" dann nächste Grenze +7 Tage
  726. $wdadd = 518400 if($wd eq "Di"); # wenn Start am "Di" dann nächste Grenze +6 Tage
  727. $wdadd = 432000 if($wd eq "Mi"); # wenn Start am "Mi" dann nächste Grenze +5 Tage
  728. $wdadd = 345600 if($wd eq "Do"); # wenn Start am "Do" dann nächste Grenze +4 Tage
  729. $wdadd = 259200 if($wd eq "Fr"); # wenn Start am "Fr" dann nächste Grenze +3 Tage
  730. $wdadd = 172800 if($wd eq "Sa"); # wenn Start am "Sa" dann nächste Grenze +2 Tage
  731. $wdadd = 86400 if($wd eq "So"); # wenn Start am "So" dann nächste Grenze +1 Tage
  732. Log3 ($name, 5, "DbRep $name - weekday of start for selection: $wd -> wdadd: $wdadd");
  733. if ($aggregation eq "hour") {
  734. $aggsec = 3600;
  735. } elsif ($aggregation eq "day") {
  736. $aggsec = 86400;
  737. } elsif ($aggregation eq "week") {
  738. $aggsec = 604800;
  739. } elsif ($aggregation eq "month") {
  740. $aggsec = 2678400;
  741. } elsif ($aggregation eq "no") {
  742. $aggsec = 1;
  743. } else {
  744. return;
  745. }
  746. my %cv = (
  747. tsstr => $tsstr,
  748. testr => $testr,
  749. dsstr => $dsstr,
  750. destr => $destr,
  751. msstr => $msstr,
  752. mestr => $mestr,
  753. ysstr => $ysstr,
  754. yestr => $yestr,
  755. aggsec => $aggsec,
  756. aggregation => $aggregation,
  757. epoch_seconds_end => $epoch_seconds_end,
  758. wdadd => $wdadd
  759. );
  760. $hash->{HELPER}{CV} = \%cv;
  761. my $ts; # für Erstellung Timestamp-Array zur nonblocking SQL-Abarbeitung
  762. my $i = 1; # Schleifenzähler -> nur Indikator für ersten Durchlauf -> anderer $runtime_string_first
  763. my $ll; # loopindikator, wenn 1 = loopausstieg
  764. # Aufbau Timestampstring mit Zeitgrenzen entsprechend Aggregation
  765. while (!$ll) {
  766. # collect aggregation strings
  767. ($runtime,$runtime_string,$runtime_string_first,$runtime_string_next,$ll) = collaggstr($hash,$runtime,$i,$runtime_string_next);
  768. $ts .= $runtime_string."#".$runtime_string_first."#".$runtime_string_next."|";
  769. $i++;
  770. }
  771. if ($opt eq "sumValue") {
  772. $hash->{HELPER}{RUNNING_PID} = BlockingCall("sumval_DoParse", "$name§$device§$reading§$ts", "sumval_ParseDone", $to, "ParseAborted", $hash);
  773. } elsif ($opt eq "countEntries") {
  774. $hash->{HELPER}{RUNNING_PID} = BlockingCall("count_DoParse", "$name§$device§$reading§$ts", "count_ParseDone", $to, "ParseAborted", $hash);
  775. } elsif ($opt eq "averageValue") {
  776. $hash->{HELPER}{RUNNING_PID} = BlockingCall("averval_DoParse", "$name§$device§$reading§$ts", "averval_ParseDone", $to, "ParseAborted", $hash);
  777. } elsif ($opt eq "fetchrows") {
  778. $runtime_string_first = defined($epoch_seconds_begin) ? strftime "%Y-%m-%d %H:%M:%S", localtime($epoch_seconds_begin) : "1970-01-01 01:00:00";
  779. $runtime_string_next = strftime "%Y-%m-%d %H:%M:%S", localtime($epoch_seconds_end);
  780. $hash->{HELPER}{RUNNING_PID} = BlockingCall("fetchrows_DoParse", "$name|$device|$reading|$runtime_string_first|$runtime_string_next", "fetchrows_ParseDone", $to, "ParseAborted", $hash);
  781. } elsif ($opt eq "exportToFile") {
  782. $runtime_string_first = defined($epoch_seconds_begin) ? strftime "%Y-%m-%d %H:%M:%S", localtime($epoch_seconds_begin) : "1970-01-01 01:00:00";
  783. $runtime_string_next = strftime "%Y-%m-%d %H:%M:%S", localtime($epoch_seconds_end);
  784. $hash->{HELPER}{RUNNING_PID} = BlockingCall("expfile_DoParse", "$name|$device|$reading|$runtime_string_first|$runtime_string_next", "expfile_ParseDone", $to, "ParseAborted", $hash);
  785. } elsif ($opt eq "importFromFile") {
  786. $hash->{HELPER}{RUNNING_PID} = BlockingCall("impfile_Push", "$name", "impfile_PushDone", $to, "ParseAborted", $hash);
  787. } elsif ($opt eq "maxValue") {
  788. $hash->{HELPER}{RUNNING_PID} = BlockingCall("maxval_DoParse", "$name§$device§$reading§$ts", "maxval_ParseDone", $to, "ParseAborted", $hash);
  789. } elsif ($opt eq "minValue") {
  790. $hash->{HELPER}{RUNNING_PID} = BlockingCall("minval_DoParse", "$name§$device§$reading§$ts", "minval_ParseDone", $to, "ParseAborted", $hash);
  791. } elsif ($opt eq "delEntries") {
  792. $runtime_string_first = defined($epoch_seconds_begin) ? strftime "%Y-%m-%d %H:%M:%S", localtime($epoch_seconds_begin) : "1970-01-01 01:00:00";
  793. $runtime_string_next = strftime "%Y-%m-%d %H:%M:%S", localtime($epoch_seconds_end);
  794. $hash->{HELPER}{RUNNING_PID} = BlockingCall("del_DoParse", "$name|$device|$reading|$runtime_string_first|$runtime_string_next", "del_ParseDone", $to, "ParseAborted", $hash);
  795. } elsif ($opt eq "diffValue") {
  796. $hash->{HELPER}{RUNNING_PID} = BlockingCall("diffval_DoParse", "$name§$device§$reading§$ts", "diffval_ParseDone", $to, "ParseAborted", $hash);
  797. } elsif ($opt eq "insert") {
  798. $hash->{HELPER}{RUNNING_PID} = BlockingCall("insert_Push", "$name", "insert_Done", $to, "ParseAborted", $hash);
  799. } elsif ($opt eq "deviceRename" || $opt eq "readingRename") {
  800. $hash->{HELPER}{RUNNING_PID} = BlockingCall("devren_Push", "$name", "devren_Done", $to, "ParseAborted", $hash);
  801. }
  802. return;
  803. }
  804. ####################################################################################################
  805. # delete Readings before new operation
  806. ####################################################################################################
  807. sub delread($) {
  808. # Readings löschen die nicht in der Ausnahmeliste (Attr readingPreventFromDel) stehen
  809. my ($hash) = @_;
  810. my $name = $hash->{NAME};
  811. my @rdpfdel = split(",", $hash->{HELPER}{RDPFDEL}) if($hash->{HELPER}{RDPFDEL});
  812. if (@rdpfdel) {
  813. my @allrds = keys%{$defs{$name}{READINGS}};
  814. foreach my $key(@allrds) {
  815. # Log3 ($name, 3, "DbRep $name - Reading Schlüssel: $key");
  816. my $dodel = 1;
  817. foreach my $rdpfdel(@rdpfdel) {
  818. if($key =~ /$rdpfdel/) {
  819. $dodel = 0;
  820. }
  821. }
  822. if($dodel) {
  823. delete($defs{$name}{READINGS}{$key});
  824. }
  825. }
  826. } else {
  827. delete $defs{$name}{READINGS};
  828. }
  829. return undef;
  830. }
  831. ####################################################################################################
  832. # nichtblockierende DB-Abfrage averageValue
  833. ####################################################################################################
  834. sub averval_DoParse($) {
  835. my ($string) = @_;
  836. my ($name, $device, $reading, $ts) = split("\\§", $string);
  837. my $hash = $defs{$name};
  838. my $dbloghash = $hash->{dbloghash};
  839. my $dbconn = $dbloghash->{dbconn};
  840. my $dbuser = $dbloghash->{dbuser};
  841. my $dblogname = $dbloghash->{NAME};
  842. my $dbpassword = $attr{"sec$dblogname"}{secret};
  843. my $err;
  844. # Background-Startzeit
  845. my $bst = [gettimeofday];
  846. Log3 ($name, 4, "DbRep $name -> Start BlockingCall averval_DoParse");
  847. my $dbh;
  848. eval {$dbh = DBI->connect("dbi:$dbconn", $dbuser, $dbpassword, { PrintError => 0, RaiseError => 1, AutoInactiveDestroy => 1 });};
  849. if ($@) {
  850. $err = encode_base64($@,"");
  851. Log3 ($name, 2, "DbRep $name - $@");
  852. Log3 ($name, 4, "DbRep $name -> BlockingCall averval_DoParse finished");
  853. return "$name|''|$device|$reading|''|$err";
  854. }
  855. # only for this block because of warnings if details of readings are not set
  856. no warnings 'uninitialized';
  857. # Timestampstring to Array
  858. my @ts = split("\\|", $ts);
  859. Log3 ($name, 5, "DbRep $name - Timestamp-Array: \n@ts");
  860. # SQL-Startzeit
  861. my $st = [gettimeofday];
  862. # DB-Abfrage zeilenweise für jeden Array-Eintrag
  863. my $arrstr;
  864. foreach my $row (@ts) {
  865. my @a = split("#", $row);
  866. my $runtime_string = $a[0];
  867. my $runtime_string_first = $a[1];
  868. my $runtime_string_next = $a[2];
  869. # SQL zusammenstellen für DB-Abfrage
  870. my $sql = "SELECT AVG(VALUE) FROM `history` where ";
  871. $sql .= "DEVICE LIKE '$device' AND " if($device);
  872. $sql .= "READING LIKE '$reading' AND " if($reading);
  873. $sql .= "TIMESTAMP >= '$runtime_string_first' AND TIMESTAMP < '$runtime_string_next' ;";
  874. Log3 ($name, 4, "DbRep $name - SQL execute: $sql");
  875. my $line;
  876. # DB-Abfrage -> Ergebnis in $arrstr aufnehmen
  877. eval {$line = $dbh->selectrow_array($sql);};
  878. if ($@) {
  879. $err = encode_base64($@,"");
  880. Log3 ($name, 2, "DbRep $name - $@");
  881. $dbh->disconnect;
  882. Log3 ($name, 4, "DbRep $name -> BlockingCall averval_DoParse finished");
  883. return "$name|''|$device|$reading|''|$err";
  884. } else {
  885. Log3 ($name, 5, "DbRep $name - SQL result: $line") if($line);
  886. if(AttrVal($name, "aggregation", "") eq "hour") {
  887. my @rsf = split(/[" "\|":"]/,$runtime_string_first);
  888. $arrstr .= $runtime_string."#".$line."#".$rsf[0]."_".$rsf[1]."|";
  889. } else {
  890. my @rsf = split(" ",$runtime_string_first);
  891. $arrstr .= $runtime_string."#".$line."#".$rsf[0]."|";
  892. }
  893. }
  894. }
  895. # SQL-Laufzeit ermitteln
  896. my $rt = tv_interval($st);
  897. $dbh->disconnect;
  898. # Daten müssen als Einzeiler zurückgegeben werden
  899. $arrstr = encode_base64($arrstr,"");
  900. Log3 ($name, 4, "DbRep $name -> BlockingCall averval_DoParse finished");
  901. # Background-Laufzeit ermitteln
  902. my $brt = tv_interval($bst);
  903. $rt = $rt.",".$brt;
  904. return "$name|$arrstr|$device|$reading|$rt|0";
  905. }
  906. ####################################################################################################
  907. # Auswertungsroutine der nichtblockierenden DB-Abfrage averageValue
  908. ####################################################################################################
  909. sub averval_ParseDone($) {
  910. my ($string) = @_;
  911. my @a = split("\\|",$string);
  912. my $hash = $defs{$a[0]};
  913. my $name = $hash->{NAME};
  914. my $arrstr = decode_base64($a[1]);
  915. my $device = $a[2];
  916. $device =~ s/%/\//g;
  917. my $reading = $a[3];
  918. $reading =~ s/%/\//g;
  919. my $bt = $a[4];
  920. my ($rt,$brt) = split(",", $bt);
  921. my $err = $a[5]?decode_base64($a[5]):undef;
  922. my $reading_runtime_string;
  923. Log3 ($name, 4, "DbRep $name -> Start BlockingCall averval_ParseDone");
  924. if ($err) {
  925. readingsSingleUpdate($hash, "errortext", $err, 1);
  926. readingsSingleUpdate($hash, "state", "error", 1);
  927. delete($hash->{HELPER}{RUNNING_PID});
  928. Log3 ($name, 4, "DbRep $name -> BlockingCall averval_ParseDone finished");
  929. return;
  930. }
  931. # only for this block because of warnings if details of readings are not set
  932. no warnings 'uninitialized';
  933. # Readingaufbereitung
  934. readingsBeginUpdate($hash);
  935. my @arr = split("\\|", $arrstr);
  936. foreach my $row (@arr) {
  937. my @a = split("#", $row);
  938. my $runtime_string = $a[0];
  939. my $c = $a[1];
  940. my $rsf = $a[2]."__";
  941. if (AttrVal($hash->{NAME}, "readingNameMap", "")) {
  942. $reading_runtime_string = $rsf.AttrVal($hash->{NAME}, "readingNameMap", "")."__".$runtime_string;
  943. } else {
  944. my $ds = $device."__" if ($device);
  945. my $rds = $reading."__" if ($reading);
  946. $reading_runtime_string = $rsf.$ds.$rds."AVERAGE__".$runtime_string;
  947. }
  948. readingsBulkUpdate($hash, $reading_runtime_string, $c?sprintf("%.4f",$c):"-");
  949. }
  950. readingsBulkUpdate($hash, "background_processing_time", sprintf("%.4f",$brt)) if(AttrVal($name, "showproctime", undef));
  951. readingsBulkUpdate($hash, "sql_processing_time", sprintf("%.4f",$rt)) if(AttrVal($name, "showproctime", undef));
  952. readingsBulkUpdate($hash, "state", "done");
  953. readingsEndUpdate($hash, 1);
  954. delete($hash->{HELPER}{RUNNING_PID});
  955. Log3 ($name, 4, "DbRep $name -> BlockingCall averval_ParseDone finished");
  956. return;
  957. }
  958. ####################################################################################################
  959. # nichtblockierende DB-Abfrage count
  960. ####################################################################################################
  961. sub count_DoParse($) {
  962. my ($string) = @_;
  963. my ($name, $device, $reading, $ts) = split("\\§", $string);
  964. my $hash = $defs{$name};
  965. my $dbloghash = $hash->{dbloghash};
  966. my $dbconn = $dbloghash->{dbconn};
  967. my $dbuser = $dbloghash->{dbuser};
  968. my $dblogname = $dbloghash->{NAME};
  969. my $dbpassword = $attr{"sec$dblogname"}{secret};
  970. my $err;
  971. # Background-Startzeit
  972. my $bst = [gettimeofday];
  973. Log3 ($name, 4, "DbRep $name -> Start BlockingCall count_DoParse");
  974. my $dbh;
  975. eval {$dbh = DBI->connect("dbi:$dbconn", $dbuser, $dbpassword, { PrintError => 0, RaiseError => 1, AutoInactiveDestroy => 1 });};
  976. if ($@) {
  977. $err = encode_base64($@,"");
  978. Log3 ($name, 2, "DbRep $name - $@");
  979. Log3 ($name, 4, "DbRep $name -> BlockingCall count_DoParse finished");
  980. return "$name|''|$device|$reading|''|$err";
  981. }
  982. # only for this block because of warnings if details of readings are not set
  983. no warnings 'uninitialized';
  984. # Timestampstring to Array
  985. my @ts = split("\\|", $ts);
  986. Log3 ($name, 5, "DbRep $name - Timestamp-Array: \n@ts");
  987. # SQL-Startzeit
  988. my $st = [gettimeofday];
  989. # DB-Abfrage zeilenweise für jeden Array-Eintrag
  990. my $arrstr;
  991. foreach my $row (@ts) {
  992. my @a = split("#", $row);
  993. my $runtime_string = $a[0];
  994. my $runtime_string_first = $a[1];
  995. my $runtime_string_next = $a[2];
  996. # SQL zusammenstellen für DB-Abfrage
  997. my $sql = "SELECT COUNT(*) FROM `history` where ";
  998. $sql .= "DEVICE LIKE '$device' AND " if($device);
  999. $sql .= "READING LIKE '$reading' AND " if($reading);
  1000. $sql .= "TIMESTAMP >= '$runtime_string_first' AND TIMESTAMP < '$runtime_string_next';";
  1001. Log3($name, 4, "DbRep $name - SQL execute: $sql");
  1002. my $line;
  1003. # DB-Abfrage -> Ergebnis in $arrstr aufnehmen
  1004. eval {$line = $dbh->selectrow_array($sql);};
  1005. if ($@) {
  1006. $err = encode_base64($@,"");
  1007. Log3 ($name, 2, "DbRep $name - $@");
  1008. $dbh->disconnect;
  1009. Log3 ($name, 4, "DbRep $name -> BlockingCall count_DoParse finished");
  1010. return "$name|''|$device|$reading|''|$err";
  1011. } else {
  1012. Log3 ($name, 5, "DbRep $name - SQL result: $line") if($line);
  1013. if(AttrVal($name, "aggregation", "") eq "hour") {
  1014. my @rsf = split(/[" "\|":"]/,$runtime_string_first);
  1015. $arrstr .= $runtime_string."#".$line."#".$rsf[0]."_".$rsf[1]."|";
  1016. } else {
  1017. my @rsf = split(" ",$runtime_string_first);
  1018. $arrstr .= $runtime_string."#".$line."#".$rsf[0]."|";
  1019. }
  1020. }
  1021. }
  1022. # SQL-Laufzeit ermitteln
  1023. my $rt = tv_interval($st);
  1024. $dbh->disconnect;
  1025. # Daten müssen als Einzeiler zurückgegeben werden
  1026. $arrstr = encode_base64($arrstr,"");
  1027. Log3 ($name, 4, "DbRep $name -> BlockingCall count_DoParse finished");
  1028. # Background-Laufzeit ermitteln
  1029. my $brt = tv_interval($bst);
  1030. $rt = $rt.",".$brt;
  1031. return "$name|$arrstr|$device|$reading|$rt|0";
  1032. }
  1033. ####################################################################################################
  1034. # Auswertungsroutine der nichtblockierenden DB-Abfrage count
  1035. ####################################################################################################
  1036. sub count_ParseDone($) {
  1037. my ($string) = @_;
  1038. my @a = split("\\|",$string);
  1039. my $hash = $defs{$a[0]};
  1040. my $name = $hash->{NAME};
  1041. my $arrstr = decode_base64($a[1]);
  1042. my $device = $a[2];
  1043. $device =~ s/%/\//g;
  1044. my $reading = $a[3];
  1045. $reading =~ s/%/\//g;
  1046. my $bt = $a[4];
  1047. my ($rt,$brt) = split(",", $bt);
  1048. my $err = $a[5]?decode_base64($a[5]):undef;
  1049. my $reading_runtime_string;
  1050. Log3 ($name, 4, "DbRep $name -> Start BlockingCall count_ParseDone");
  1051. if ($err) {
  1052. readingsSingleUpdate($hash, "errortext", $err, 1);
  1053. readingsSingleUpdate($hash, "state", "error", 1);
  1054. delete($hash->{HELPER}{RUNNING_PID});
  1055. Log3 ($name, 4, "DbRep $name -> BlockingCall count_ParseDone finished");
  1056. return;
  1057. }
  1058. Log3 ($name, 5, "DbRep $name - SQL result decoded: $arrstr") if($arrstr);
  1059. # only for this block because of warnings if details of readings are not set
  1060. no warnings 'uninitialized';
  1061. # Readingaufbereitung
  1062. readingsBeginUpdate($hash);
  1063. my @arr = split("\\|", $arrstr);
  1064. foreach my $row (@arr) {
  1065. my @a = split("#", $row);
  1066. my $runtime_string = $a[0];
  1067. my $c = $a[1];
  1068. my $rsf = $a[2]."__";
  1069. if (AttrVal($hash->{NAME}, "readingNameMap", "")) {
  1070. $reading_runtime_string = $rsf.AttrVal($hash->{NAME}, "readingNameMap", "")."__".$runtime_string;
  1071. } else {
  1072. my $ds = $device."__" if ($device);
  1073. my $rds = $reading."__" if ($reading);
  1074. $reading_runtime_string = $rsf.$ds.$rds."COUNT__".$runtime_string;
  1075. }
  1076. readingsBulkUpdate($hash, $reading_runtime_string, $c?$c:"-");
  1077. }
  1078. readingsBulkUpdate($hash, "background_processing_time", sprintf("%.4f",$brt)) if(AttrVal($name, "showproctime", undef));
  1079. readingsBulkUpdate($hash, "sql_processing_time", sprintf("%.4f",$rt)) if(AttrVal($name, "showproctime", undef));
  1080. readingsBulkUpdate($hash, "state", "done");
  1081. readingsEndUpdate($hash, 1);
  1082. delete($hash->{HELPER}{RUNNING_PID});
  1083. Log3 ($name, 4, "DbRep $name -> BlockingCall count_ParseDone finished");
  1084. return;
  1085. }
  1086. ####################################################################################################
  1087. # nichtblockierende DB-Abfrage maxValue
  1088. ####################################################################################################
  1089. sub maxval_DoParse($) {
  1090. my ($string) = @_;
  1091. my ($name, $device, $reading, $ts) = split("\\§", $string);
  1092. my $hash = $defs{$name};
  1093. my $dbloghash = $hash->{dbloghash};
  1094. my $dbconn = $dbloghash->{dbconn};
  1095. my $dbuser = $dbloghash->{dbuser};
  1096. my $dblogname = $dbloghash->{NAME};
  1097. my $dbpassword = $attr{"sec$dblogname"}{secret};
  1098. my $err;
  1099. # Background-Startzeit
  1100. my $bst = [gettimeofday];
  1101. Log3 ($name, 4, "DbRep $name -> Start BlockingCall maxval_DoParse");
  1102. my $dbh;
  1103. eval {$dbh = DBI->connect("dbi:$dbconn", $dbuser, $dbpassword, { PrintError => 0, RaiseError => 1, AutoInactiveDestroy => 1 });};
  1104. if ($@) {
  1105. $err = encode_base64($@,"");
  1106. Log3 ($name, 2, "DbRep $name - $@");
  1107. Log3 ($name, 4, "DbRep $name -> BlockingCall maxval_DoParse finished");
  1108. return "$name|''|$device|$reading|''|$err";
  1109. }
  1110. # only for this block because of warnings if details of readings are not set
  1111. no warnings 'uninitialized';
  1112. # Timestampstring to Array
  1113. my @ts = split("\\|", $ts);
  1114. Log3 ($name, 5, "DbRep $name - Timestamp-Array: \n@ts");
  1115. # SQL-Startzeit
  1116. my $st = [gettimeofday];
  1117. # DB-Abfrage zeilenweise für jeden Array-Eintrag
  1118. my @row_array;
  1119. foreach my $row (@ts) {
  1120. my @a = split("#", $row);
  1121. my $runtime_string = $a[0];
  1122. my $runtime_string_first = $a[1];
  1123. my $runtime_string_next = $a[2];
  1124. # SQL zusammenstellen für DB-Operation
  1125. my $sql = "SELECT VALUE,TIMESTAMP FROM `history` where ";
  1126. $sql .= "`DEVICE` LIKE '$device' AND " if($device);
  1127. $sql .= "`READING` LIKE '$reading' AND " if($reading);
  1128. $sql .= "TIMESTAMP >= ? AND TIMESTAMP < ? ORDER BY TIMESTAMP ;";
  1129. # SQL zusammenstellen für Logausgabe
  1130. my $sql1 = "SELECT VALUE,TIMESTAMP FROM `history` where ";
  1131. $sql1 .= "`DEVICE` LIKE '$device' AND " if($device);
  1132. $sql1 .= "`READING` LIKE '$reading' AND " if($reading);
  1133. $sql1 .= "TIMESTAMP >= '$runtime_string_first' AND TIMESTAMP < '$runtime_string_next' ORDER BY TIMESTAMP;";
  1134. Log3 ($name, 4, "DbRep $name - SQL execute: $sql1");
  1135. $runtime_string = encode_base64($runtime_string,"");
  1136. my $sth = $dbh->prepare($sql);
  1137. eval {$sth->execute($runtime_string_first, $runtime_string_next);};
  1138. if ($@) {
  1139. $err = encode_base64($@,"");
  1140. Log3 ($name, 2, "DbRep $name - $@");
  1141. $dbh->disconnect;
  1142. Log3 ($name, 4, "DbRep $name -> BlockingCall maxval_DoParse finished");
  1143. return "$name|''|$device|$reading|''|$err";
  1144. } else {
  1145. my @array= map { $runtime_string." ".$_ -> [0]." ".$_ -> [1]."\n" } @{ $sth->fetchall_arrayref() };
  1146. if(!@array) {
  1147. if(AttrVal($name, "aggregation", "") eq "hour") {
  1148. my @rsf = split(/[" "\|":"]/,$runtime_string_first);
  1149. @array = ($runtime_string." "."0"." ".$rsf[0]."_".$rsf[1]."\n");
  1150. } else {
  1151. my @rsf = split(" ",$runtime_string_first);
  1152. @array = ($runtime_string." "."0"." ".$rsf[0]."\n");
  1153. }
  1154. }
  1155. push(@row_array, @array);
  1156. }
  1157. }
  1158. # SQL-Laufzeit ermitteln
  1159. my $rt = tv_interval($st);
  1160. $dbh->disconnect;
  1161. Log3 ($name, 5, "DbRep $name -> raw data of row_array result:\n @row_array");
  1162. #---------- Berechnung Ergebnishash maxValue ------------------------
  1163. my $i = 1;
  1164. my %rh = ();
  1165. my $lastruntimestring;
  1166. my $row_max_time;
  1167. my $max_value = 0;
  1168. foreach my $row (@row_array) {
  1169. my @a = split("[ \t][ \t]*", $row);
  1170. my $runtime_string = decode_base64($a[0]);
  1171. $lastruntimestring = $runtime_string if ($i == 1);
  1172. my $value = $a[1];
  1173. $a[3] =~ s/:/-/g if($a[3]); # substituieren unsopported characters -> siehe fhem.pl
  1174. my $timestamp = $a[3]?$a[2]."_".$a[3]:$a[2];
  1175. # Leerzeichen am Ende $timestamp entfernen
  1176. $timestamp =~ s/\s+$//g;
  1177. # Test auf $value = "numeric"
  1178. if (!looks_like_number($value)) {
  1179. $a[3] =~ s/\s+$//g;
  1180. Log3 ($name, 2, "DbRep $name - ERROR - value isn't numeric in maxValue function. Faulty dataset was \nTIMESTAMP: $timestamp, DEVICE: $device, READING: $reading, VALUE: $value.");
  1181. $err = encode_base64("Value isn't numeric. Faulty dataset was - TIMESTAMP: $timestamp, VALUE: $value", "");
  1182. Log3 ($name, 4, "DbRep $name -> BlockingCall maxval_DoParse finished");
  1183. return "$name|''|$device|$reading|''|$err";
  1184. }
  1185. Log3 ($name, 5, "DbRep $name - Runtimestring: $runtime_string, DEVICE: $device, READING: $reading, TIMESTAMP: $timestamp, VALUE: $value");
  1186. if ($runtime_string eq $lastruntimestring) {
  1187. if ($value >= $max_value) {
  1188. $max_value = $value;
  1189. $row_max_time = $timestamp;
  1190. $rh{$runtime_string} = $runtime_string."|".$max_value."|".$row_max_time;
  1191. }
  1192. } else {
  1193. # neuer Zeitabschnitt beginnt, ersten Value-Wert erfassen
  1194. $lastruntimestring = $runtime_string;
  1195. $max_value = 0;
  1196. if ($value >= $max_value) {
  1197. $max_value = $value;
  1198. $row_max_time = $timestamp;
  1199. $rh{$runtime_string} = $runtime_string."|".$max_value."|".$row_max_time;
  1200. }
  1201. }
  1202. $i++;
  1203. }
  1204. #---------------------------------------------------------------------------------------------
  1205. Log3 ($name, 5, "DbRep $name - result of maxValue calculation before encoding:");
  1206. foreach my $key (sort(keys(%rh))) {
  1207. Log3 ($name, 5, "runtimestring Key: $key, value: ".$rh{$key});
  1208. }
  1209. # Ergebnishash als Einzeiler zurückgeben
  1210. my $rows = join('§', %rh);
  1211. my $rowlist = encode_base64($rows,"");
  1212. Log3 ($name, 4, "DbRep $name -> BlockingCall maxval_DoParse finished");
  1213. # Background-Laufzeit ermitteln
  1214. my $brt = tv_interval($bst);
  1215. $rt = $rt.",".$brt;
  1216. return "$name|$rowlist|$device|$reading|$rt|0";
  1217. }
  1218. ####################################################################################################
  1219. # Auswertungsroutine der nichtblockierenden DB-Abfrage maxValue
  1220. ####################################################################################################
  1221. sub maxval_ParseDone($) {
  1222. my ($string) = @_;
  1223. my @a = split("\\|",$string);
  1224. my $hash = $defs{$a[0]};
  1225. my $name = $hash->{NAME};
  1226. my $rowlist = decode_base64($a[1]);
  1227. my $device = $a[2];
  1228. $device =~ s/%/\//g;
  1229. my $reading = $a[3];
  1230. $reading =~ s/%/\//g;
  1231. my $bt = $a[4];
  1232. my ($rt,$brt) = split(",", $bt);
  1233. my $err = $a[5]?decode_base64($a[5]):undef;
  1234. my $reading_runtime_string;
  1235. Log3 ($name, 4, "DbRep $name -> Start BlockingCall maxval_ParseDone");
  1236. if ($err) {
  1237. readingsSingleUpdate($hash, "errortext", $err, 1);
  1238. readingsSingleUpdate($hash, "state", "error", 1);
  1239. delete($hash->{HELPER}{RUNNING_PID});
  1240. Log3 ($name, 4, "DbRep $name -> BlockingCall maxval_ParseDone finished");
  1241. return;
  1242. }
  1243. my %rh = split("§", $rowlist);
  1244. Log3 ($name, 5, "DbRep $name - result of maxValue calculation after decoding:");
  1245. foreach my $key (sort(keys(%rh))) {
  1246. Log3 ($name, 5, "DbRep $name - runtimestring Key: $key, value: ".$rh{$key});
  1247. }
  1248. # Readingaufbereitung
  1249. readingsBeginUpdate($hash);
  1250. # only for this block because of warnings if details of readings are not set
  1251. no warnings 'uninitialized';
  1252. foreach my $key (sort(keys(%rh))) {
  1253. my @k = split("\\|",$rh{$key});
  1254. my $rsf = $k[2]."__" if($k[2]);
  1255. if (AttrVal($hash->{NAME}, "readingNameMap", "")) {
  1256. $reading_runtime_string = $rsf.AttrVal($hash->{NAME}, "readingNameMap", "")."__".$k[0];
  1257. } else {
  1258. my $ds = $device."__" if ($device);
  1259. my $rds = $reading."__" if ($reading);
  1260. $reading_runtime_string = $rsf.$ds.$rds."MAX__".$k[0];
  1261. }
  1262. my $rv = $k[1];
  1263. readingsBulkUpdate($hash, $reading_runtime_string, $rv?sprintf("%.4f",$rv):"-");
  1264. }
  1265. readingsBulkUpdate($hash, "background_processing_time", sprintf("%.4f",$brt)) if(AttrVal($name, "showproctime", undef));
  1266. readingsBulkUpdate($hash, "sql_processing_time", sprintf("%.4f",$rt)) if(AttrVal($name, "showproctime", undef));
  1267. readingsBulkUpdate($hash, "state", "done");
  1268. readingsEndUpdate($hash, 1);
  1269. delete($hash->{HELPER}{RUNNING_PID});
  1270. Log3 ($name, 4, "DbRep $name -> BlockingCall maxval_ParseDone finished");
  1271. return;
  1272. }
  1273. ####################################################################################################
  1274. # nichtblockierende DB-Abfrage minValue
  1275. ####################################################################################################
  1276. sub minval_DoParse($) {
  1277. my ($string) = @_;
  1278. my ($name, $device, $reading, $ts) = split("\\§", $string);
  1279. my $hash = $defs{$name};
  1280. my $dbloghash = $hash->{dbloghash};
  1281. my $dbconn = $dbloghash->{dbconn};
  1282. my $dbuser = $dbloghash->{dbuser};
  1283. my $dblogname = $dbloghash->{NAME};
  1284. my $dbpassword = $attr{"sec$dblogname"}{secret};
  1285. my $err;
  1286. # Background-Startzeit
  1287. my $bst = [gettimeofday];
  1288. Log3 ($name, 4, "DbRep $name -> Start BlockingCall minval_DoParse");
  1289. my $dbh;
  1290. eval {$dbh = DBI->connect("dbi:$dbconn", $dbuser, $dbpassword, { PrintError => 0, RaiseError => 1, AutoInactiveDestroy => 1 });};
  1291. if ($@) {
  1292. $err = encode_base64($@,"");
  1293. Log3 ($name, 2, "DbRep $name - $@");
  1294. Log3 ($name, 4, "DbRep $name -> BlockingCall minval_DoParse finished");
  1295. return "$name|''|$device|$reading|''|$err";
  1296. }
  1297. # only for this block because of warnings if details of readings are not set
  1298. no warnings 'uninitialized';
  1299. # Timestampstring to Array
  1300. my @ts = split("\\|", $ts);
  1301. Log3 ($name, 5, "DbRep $name - Timestamp-Array: \n@ts");
  1302. # SQL-Startzeit
  1303. my $st = [gettimeofday];
  1304. # DB-Abfrage zeilenweise für jeden Array-Eintrag
  1305. my @row_array;
  1306. foreach my $row (@ts) {
  1307. my @a = split("#", $row);
  1308. my $runtime_string = $a[0];
  1309. my $runtime_string_first = $a[1];
  1310. my $runtime_string_next = $a[2];
  1311. # SQL zusammenstellen für DB-Operation
  1312. my $sql = "SELECT VALUE,TIMESTAMP FROM `history` where ";
  1313. $sql .= "`DEVICE` LIKE '$device' AND " if($device);
  1314. $sql .= "`READING` LIKE '$reading' AND " if($reading);
  1315. $sql .= "TIMESTAMP >= ? AND TIMESTAMP < ? ORDER BY TIMESTAMP ;";
  1316. # SQL zusammenstellen für Logausgabe
  1317. my $sql1 = "SELECT VALUE,TIMESTAMP FROM `history` where ";
  1318. $sql1 .= "`DEVICE` LIKE '$device' AND " if($device);
  1319. $sql1 .= "`READING` LIKE '$reading' AND " if($reading);
  1320. $sql1 .= "TIMESTAMP >= '$runtime_string_first' AND TIMESTAMP < '$runtime_string_next' ORDER BY TIMESTAMP;";
  1321. Log3 ($name, 4, "DbRep $name - SQL execute: $sql1");
  1322. $runtime_string = encode_base64($runtime_string,"");
  1323. my $sth = $dbh->prepare($sql);
  1324. eval {$sth->execute($runtime_string_first, $runtime_string_next);};
  1325. if ($@) {
  1326. $err = encode_base64($@,"");
  1327. Log3 ($name, 2, "DbRep $name - $@");
  1328. $dbh->disconnect;
  1329. Log3 ($name, 4, "DbRep $name -> BlockingCall minval_DoParse finished");
  1330. return "$name|''|$device|$reading|''|$err";
  1331. } else {
  1332. my @array= map { $runtime_string." ".$_ -> [0]." ".$_ -> [1]."\n" } @{ $sth->fetchall_arrayref() };
  1333. if(!@array) {
  1334. if(AttrVal($name, "aggregation", "") eq "hour") {
  1335. my @rsf = split(/[" "\|":"]/,$runtime_string_first);
  1336. @array = ($runtime_string." "."0"." ".$rsf[0]."_".$rsf[1]."\n");
  1337. } else {
  1338. my @rsf = split(" ",$runtime_string_first);
  1339. @array = ($runtime_string." "."0"." ".$rsf[0]."\n");
  1340. }
  1341. }
  1342. push(@row_array, @array);
  1343. }
  1344. }
  1345. # SQL-Laufzeit ermitteln
  1346. my $rt = tv_interval($st);
  1347. $dbh->disconnect;
  1348. Log3 ($name, 5, "DbRep $name -> raw data of row_array result:\n @row_array");
  1349. #---------- Berechnung Ergebnishash minValue ------------------------
  1350. my $i = 1;
  1351. my %rh = ();
  1352. my $lastruntimestring;
  1353. my $row_min_time;
  1354. my ($min_value,$value);
  1355. foreach my $row (@row_array) {
  1356. my @a = split("[ \t][ \t]*", $row);
  1357. my $runtime_string = decode_base64($a[0]);
  1358. $lastruntimestring = $runtime_string if ($i == 1);
  1359. $value = $a[1];
  1360. $min_value = $a[1] if ($i == 1);
  1361. $a[3] =~ s/:/-/g if($a[3]); # substituieren unsopported characters -> siehe fhem.pl
  1362. my $timestamp = $a[3]?$a[2]."_".$a[3]:$a[2];
  1363. # Leerzeichen am Ende $timestamp entfernen
  1364. $timestamp =~ s/\s+$//g;
  1365. # Test auf $value = "numeric"
  1366. if (!looks_like_number($value)) {
  1367. $a[3] =~ s/\s+$//g;
  1368. Log3 ($name, 2, "DbRep $name - ERROR - value isn't numeric in minValue function. Faulty dataset was \nTIMESTAMP: $timestamp, DEVICE: $device, READING: $reading, VALUE: $value.");
  1369. $err = encode_base64("Value isn't numeric. Faulty dataset was - TIMESTAMP: $timestamp, VALUE: $value", "");
  1370. Log3 ($name, 4, "DbRep $name -> BlockingCall minval_DoParse finished");
  1371. return "$name|''|$device|$reading|''|$err";
  1372. }
  1373. Log3 ($name, 5, "DbRep $name - Runtimestring: $runtime_string, DEVICE: $device, READING: $reading, TIMESTAMP: $timestamp, VALUE: $value");
  1374. $rh{$runtime_string} = $runtime_string."|".$min_value."|".$timestamp if ($i == 1); # minValue des ersten SQL-Statements in hash einfügen
  1375. if ($runtime_string eq $lastruntimestring) {
  1376. if ($value < $min_value) {
  1377. $min_value = $value;
  1378. $row_min_time = $timestamp;
  1379. $rh{$runtime_string} = $runtime_string."|".$min_value."|".$row_min_time;
  1380. }
  1381. } else {
  1382. # neuer Zeitabschnitt beginnt, ersten Value-Wert erfassen
  1383. $lastruntimestring = $runtime_string;
  1384. $min_value = $value;
  1385. $row_min_time = $timestamp;
  1386. $rh{$runtime_string} = $runtime_string."|".$min_value."|".$row_min_time;
  1387. }
  1388. $i++;
  1389. }
  1390. #---------------------------------------------------------------------------------------------
  1391. Log3 ($name, 5, "DbRep $name - result of minValue calculation before encoding:");
  1392. foreach my $key (sort(keys(%rh))) {
  1393. Log3 ($name, 5, "runtimestring Key: $key, value: ".$rh{$key});
  1394. }
  1395. # Ergebnishash als Einzeiler zurückgeben
  1396. my $rows = join('§', %rh);
  1397. my $rowlist = encode_base64($rows,"");
  1398. Log3 ($name, 4, "DbRep $name -> BlockingCall minval_DoParse finished");
  1399. # Background-Laufzeit ermitteln
  1400. my $brt = tv_interval($bst);
  1401. $rt = $rt.",".$brt;
  1402. return "$name|$rowlist|$device|$reading|$rt|0";
  1403. }
  1404. ####################################################################################################
  1405. # Auswertungsroutine der nichtblockierenden DB-Abfrage minValue
  1406. ####################################################################################################
  1407. sub minval_ParseDone($) {
  1408. my ($string) = @_;
  1409. my @a = split("\\|",$string);
  1410. my $hash = $defs{$a[0]};
  1411. my $name = $hash->{NAME};
  1412. my $rowlist = decode_base64($a[1]);
  1413. my $device = $a[2];
  1414. $device =~ s/%/\//g;
  1415. my $reading = $a[3];
  1416. $reading =~ s/%/\//g;
  1417. my $bt = $a[4];
  1418. my ($rt,$brt) = split(",", $bt);
  1419. my $err = $a[5]?decode_base64($a[5]):undef;
  1420. my $reading_runtime_string;
  1421. Log3 ($name, 4, "DbRep $name -> Start BlockingCall minval_ParseDone");
  1422. if ($err) {
  1423. readingsSingleUpdate($hash, "errortext", $err, 1);
  1424. readingsSingleUpdate($hash, "state", "error", 1);
  1425. delete($hash->{HELPER}{RUNNING_PID});
  1426. Log3 ($name, 4, "DbRep $name -> BlockingCall minval_ParseDone finished");
  1427. return;
  1428. }
  1429. my %rh = split("§", $rowlist);
  1430. Log3 ($name, 5, "DbRep $name - result of minValue calculation after decoding:");
  1431. foreach my $key (sort(keys(%rh))) {
  1432. Log3 ($name, 5, "DbRep $name - runtimestring Key: $key, value: ".$rh{$key});
  1433. }
  1434. # Readingaufbereitung
  1435. readingsBeginUpdate($hash);
  1436. # only for this block because of warnings if details of readings are not set
  1437. no warnings 'uninitialized';
  1438. foreach my $key (sort(keys(%rh))) {
  1439. my @k = split("\\|",$rh{$key});
  1440. my $rsf = $k[2]."__" if($k[2]);
  1441. if (AttrVal($hash->{NAME}, "readingNameMap", "")) {
  1442. $reading_runtime_string = $rsf.AttrVal($hash->{NAME}, "readingNameMap", "")."__".$k[0];
  1443. } else {
  1444. my $ds = $device."__" if ($device);
  1445. my $rds = $reading."__" if ($reading);
  1446. $reading_runtime_string = $rsf.$ds.$rds."MIN__".$k[0];
  1447. }
  1448. my $rv = $k[1];
  1449. readingsBulkUpdate($hash, $reading_runtime_string, $rv?sprintf("%.4f",$rv):"-");
  1450. }
  1451. readingsBulkUpdate($hash, "background_processing_time", sprintf("%.4f",$brt)) if(AttrVal($name, "showproctime", undef));
  1452. readingsBulkUpdate($hash, "sql_processing_time", sprintf("%.4f",$rt)) if(AttrVal($name, "showproctime", undef));
  1453. readingsBulkUpdate($hash, "state", "done");
  1454. readingsEndUpdate($hash, 1);
  1455. delete($hash->{HELPER}{RUNNING_PID});
  1456. Log3 ($name, 4, "DbRep $name -> BlockingCall minval_ParseDone finished");
  1457. return;
  1458. }
  1459. ####################################################################################################
  1460. # nichtblockierende DB-Abfrage diffValue
  1461. ####################################################################################################
  1462. sub diffval_DoParse($) {
  1463. my ($string) = @_;
  1464. my ($name, $device, $reading, $ts) = split("\\§", $string);
  1465. my $hash = $defs{$name};
  1466. my $dbloghash = $hash->{dbloghash};
  1467. my $dbconn = $dbloghash->{dbconn};
  1468. my $dbuser = $dbloghash->{dbuser};
  1469. my $dblogname = $dbloghash->{NAME};
  1470. my $dbmodel = $dbloghash->{DBMODEL};
  1471. my $dbpassword = $attr{"sec$dblogname"}{secret};
  1472. my $err;
  1473. # Background-Startzeit
  1474. my $bst = [gettimeofday];
  1475. Log3 ($name, 4, "DbRep $name -> Start BlockingCall diffval_DoParse");
  1476. my $dbh;
  1477. eval {$dbh = DBI->connect("dbi:$dbconn", $dbuser, $dbpassword, { PrintError => 0, RaiseError => 1, AutoInactiveDestroy => 1 });};
  1478. if ($@) {
  1479. $err = encode_base64($@,"");
  1480. Log3 ($name, 2, "DbRep $name - $@");
  1481. Log3 ($name, 4, "DbRep $name -> BlockingCall diffval_DoParse finished");
  1482. return "$name|''|$device|$reading|''|''|''|$err";
  1483. }
  1484. # only for this block because of warnings if details of readings are not set
  1485. no warnings 'uninitialized';
  1486. # Timestampstring to Array
  1487. my @ts = split("\\|", $ts);
  1488. Log3 ($name, 5, "DbRep $name - Timestamp-Array: \n@ts");
  1489. # SQL-Startzeit
  1490. my $st = [gettimeofday];
  1491. # SQL zusammenstellen für DB-Operation neu diffValue + prepare
  1492. my $sql;
  1493. if($dbmodel ne "SQLITE") {
  1494. $sql = "SELECT TIMESTAMP,VALUE,
  1495. if(VALUE-\@V < 0 OR \@RB = 1 , \@diff:= 0, \@diff:= VALUE-\@V ) as DIFF,
  1496. \@V:= VALUE as VALUEBEFORE,
  1497. \@RB:= '0' as RBIT
  1498. FROM `history` where ";
  1499. $sql .= "`DEVICE` LIKE '$device' AND " if($device);
  1500. $sql .= "`READING` LIKE '$reading' AND " if($reading);
  1501. $sql .= "TIMESTAMP BETWEEN ? AND ? ORDER BY TIMESTAMP ;";
  1502. } else {
  1503. $sql = "SELECT TIMESTAMP,VALUE FROM `history` where ";
  1504. $sql .= "`DEVICE` LIKE '$device' AND " if($device);
  1505. $sql .= "`READING` LIKE '$reading' AND " if($reading);
  1506. $sql .= "TIMESTAMP BETWEEN ? AND ? ORDER BY TIMESTAMP ;";
  1507. }
  1508. my $sth = $dbh->prepare($sql);
  1509. # DB-Abfrage zeilenweise für jeden Array-Eintrag
  1510. my @row_array;
  1511. my @array;
  1512. foreach my $row (@ts) {
  1513. my @a = split("#", $row);
  1514. my $runtime_string = $a[0];
  1515. my $runtime_string_first = $a[1];
  1516. my $runtime_string_next = $a[2];
  1517. $runtime_string = encode_base64($runtime_string,"");
  1518. # SQL zusammenstellen für Logausgabe
  1519. my $sql1 = "SELECT ... where ";
  1520. $sql1 .= "`DEVICE` LIKE '$device' AND " if($device);
  1521. $sql1 .= "`READING` LIKE '$reading' AND " if($reading);
  1522. $sql1 .= "TIMESTAMP BETWEEN '$runtime_string_first' AND '$runtime_string_next' ORDER BY TIMESTAMP;";
  1523. Log3 ($name, 4, "DbRep $name - SQL execute: $sql1");
  1524. if($dbmodel ne "SQLITE") {
  1525. eval {$dbh->do("set \@V:= 0, \@diff:= 0, \@diffTotal:= 0, \@RB:= 1;");}; # @\RB = Resetbit wenn neues Selektionsintervall beginnt
  1526. }
  1527. if ($@) {
  1528. $err = encode_base64($@,"");
  1529. Log3 ($name, 2, "DbRep $name - $@");
  1530. $dbh->disconnect;
  1531. Log3 ($name, 4, "DbRep $name -> BlockingCall diffval_DoParse finished");
  1532. return "$name|''|$device|$reading|''|''|''|$err";
  1533. }
  1534. eval {$sth->execute($runtime_string_first, $runtime_string_next);};
  1535. if ($@) {
  1536. $err = encode_base64($@,"");
  1537. Log3 ($name, 2, "DbRep $name - $@");
  1538. $dbh->disconnect;
  1539. Log3 ($name, 4, "DbRep $name -> BlockingCall diffval_DoParse finished");
  1540. return "$name|''|$device|$reading|''|''|''|$err";
  1541. } else {
  1542. if($dbmodel ne "SQLITE") {
  1543. @array = map { $runtime_string." ".$_ -> [0]." ".$_ -> [1]." ".$_ -> [2]."\n" } @{ $sth->fetchall_arrayref() };
  1544. } else {
  1545. @array = map { $runtime_string." ".$_ -> [0]." ".$_ -> [1]."\n" } @{ $sth->fetchall_arrayref() };
  1546. if (@array) {
  1547. my @sp;
  1548. my $dse = 0;
  1549. my $vold;
  1550. my @sqlite_array;
  1551. foreach my $row (@array) {
  1552. @sp = split("[ \t][ \t]*", $row, 4);
  1553. my $runtime_string = $sp[0];
  1554. my $timestamp = $sp[2]?$sp[1]." ".$sp[2]:$sp[1];
  1555. my $vnew = $sp[3];
  1556. $vnew =~ tr/\n//d;
  1557. $dse = ($vold && (($vnew-$vold) > 0))?($vnew-$vold):0;
  1558. @sp = $runtime_string." ".$timestamp." ".$vnew." ".$dse."\n";
  1559. $vold = $vnew;
  1560. push(@sqlite_array, @sp);
  1561. }
  1562. @array = @sqlite_array;
  1563. }
  1564. }
  1565. if(!@array) {
  1566. if(AttrVal($name, "aggregation", "") eq "hour") {
  1567. my @rsf = split(/[" "\|":"]/,$runtime_string_first);
  1568. @array = ($runtime_string." ".$rsf[0]."_".$rsf[1]."\n");
  1569. } else {
  1570. my @rsf = split(" ",$runtime_string_first);
  1571. @array = ($runtime_string." ".$rsf[0]."\n");
  1572. }
  1573. }
  1574. push(@row_array, @array);
  1575. }
  1576. }
  1577. # SQL-Laufzeit ermitteln
  1578. my $rt = tv_interval($st);
  1579. $dbh->disconnect;
  1580. Log3 ($name, 5, "DbRep $name - raw data of row_array result:\n @row_array");
  1581. my $difflimit = AttrVal($name, "diffAccept", "20"); # legt fest, bis zu welchem Wert Differenzen akzeptiert werden (Ausreißer eliminieren)
  1582. # Berechnung diffValue aus Selektionshash
  1583. my %rh = (); # Ergebnishash, wird alle Ergebniszeilen enthalten
  1584. my %ch = (); # counthash, enthält die Anzahl der verarbeiteten Datasets pro runtime_string
  1585. my $lastruntimestring;
  1586. my $i = 1;
  1587. my $lval; # immer der letzte Wert von $value
  1588. my $rslval; # runtimestring von lval
  1589. my $uediff; # Übertragsdifferenz (Differenz zwischen letzten Wert einer Aggregationsperiode und dem ersten Wert der Folgeperiode)
  1590. my $diff_current; # Differenzwert des aktuellen Datasets
  1591. my $diff_before; # Differenzwert vorheriger Datensatz
  1592. my $rejectstr; # String der ignorierten Differenzsätze
  1593. my $diff_total; # Summenwert aller berücksichtigten Teildifferenzen
  1594. my $max = ($#row_array)+1; # Anzahl aller Listenelemente
  1595. Log3 ($name, 5, "DbRep $name - data of row_array result assigned to fields:\n");
  1596. foreach my $row (@row_array) {
  1597. my @a = split("[ \t][ \t]*", $row, 6);
  1598. my $runtime_string = decode_base64($a[0]);
  1599. $lastruntimestring = $runtime_string if ($i == 1);
  1600. my $timestamp = $a[2]?$a[1]."_".$a[2]:$a[1];
  1601. my $value = $a[3]?$a[3]:0;
  1602. my $diff = $a[4]?sprintf("%.4f",$a[4]):0;
  1603. # if ($uediff) {
  1604. # $diff = $diff + $uediff;
  1605. # Log3 ($name, 4, "DbRep $name - balance difference of $uediff between $rslval and $runtime_string");
  1606. # $uediff = 0;
  1607. # }
  1608. # Leerzeichen am Ende $timestamp entfernen
  1609. $timestamp =~ s/\s+$//g;
  1610. # Test auf $value = "numeric"
  1611. if (!looks_like_number($value)) {
  1612. $a[3] =~ s/\s+$//g;
  1613. Log3 ($name, 2, "DbRep $name - ERROR - value isn't numeric in diffValue function. Faulty dataset was \nTIMESTAMP: $timestamp, DEVICE: $device, READING: $reading, VALUE: $value.");
  1614. $err = encode_base64("Value isn't numeric. Faulty dataset was - TIMESTAMP: $timestamp, VALUE: $value", "");
  1615. Log3 ($name, 4, "DbRep $name -> BlockingCall diffval_DoParse finished");
  1616. return "$name|''|$device|$reading|''|''|''|$err";
  1617. }
  1618. Log3 ($name, 5, "DbRep $name - Runtimestring: $runtime_string, DEVICE: $device, READING: $reading, \nTIMESTAMP: $timestamp, VALUE: $value, DIFF: $diff");
  1619. # String ignorierter Zeilen erzeugen
  1620. $diff_current = $timestamp." ".$diff;
  1621. if($diff > $difflimit) {
  1622. $rejectstr .= $diff_before." -> ".$diff_current."\n";
  1623. }
  1624. $diff_before = $diff_current;
  1625. # Ergebnishash erzeugen
  1626. if ($runtime_string eq $lastruntimestring) {
  1627. if ($i == 1) {
  1628. $diff_total = $diff?$diff:0 if($diff <= $difflimit);
  1629. $rh{$runtime_string} = $runtime_string."|".$diff_total."|".$timestamp;
  1630. $ch{$runtime_string} = 1 if($value);
  1631. $lval = $value;
  1632. $rslval = $runtime_string;
  1633. }
  1634. if ($diff) {
  1635. if($diff <= $difflimit) {
  1636. $diff_total = $diff_total+$diff;
  1637. }
  1638. $rh{$runtime_string} = $runtime_string."|".$diff_total."|".$timestamp;
  1639. $ch{$runtime_string}++ if($value && $i > 1);
  1640. $lval = $value;
  1641. $rslval = $runtime_string;
  1642. }
  1643. } else {
  1644. # neuer Zeitabschnitt beginnt, ersten Value-Wert erfassen und Übertragsdifferenz bilden
  1645. $lastruntimestring = $runtime_string;
  1646. $i = 1;
  1647. $uediff = $value - $lval if($value > $lval);
  1648. $diff = $uediff;
  1649. $lval = $value if($value); # Übetrag über Perioden mit value = 0 hinweg !
  1650. $rslval = $runtime_string;
  1651. Log3 ($name, 4, "DbRep $name - balance difference of $uediff between $rslval and $runtime_string");
  1652. $diff_total = $diff?$diff:0 if($diff <= $difflimit);
  1653. $rh{$runtime_string} = $runtime_string."|".$diff_total."|".$timestamp;
  1654. $ch{$runtime_string} = 1 if($value);
  1655. $uediff = 0;
  1656. }
  1657. $i++;
  1658. }
  1659. Log3 ($name, 4, "DbRep $name - result of diffValue calculation before encoding:");
  1660. foreach my $key (sort(keys(%rh))) {
  1661. Log3 ($name, 4, "runtimestring Key: $key, value: ".$rh{$key});
  1662. }
  1663. my $ncp = calcount($hash,\%ch);
  1664. my ($ncps,$ncpslist);
  1665. if(%$ncp) {
  1666. Log3 ($name, 3, "DbRep $name - time/aggregation periods containing only one dataset -> no diffValue calc was possible in period:");
  1667. foreach my $key (sort(keys%{$ncp})) {
  1668. Log3 ($name, 3, $key) ;
  1669. }
  1670. $ncps = join('§', %$ncp);
  1671. $ncpslist = encode_base64($ncps,"");
  1672. }
  1673. # Ergebnishash als Einzeiler zurückgeben
  1674. # ignorierte Zeilen ($diff > $difflimit)
  1675. my $rowsrej = encode_base64($rejectstr,"") if($rejectstr);
  1676. # Ergebnishash
  1677. my $rows = join('§', %rh);
  1678. my $rowlist = encode_base64($rows,"");
  1679. Log3 ($name, 4, "DbRep $name -> BlockingCall diffval_DoParse finished");
  1680. # Background-Laufzeit ermitteln
  1681. my $brt = tv_interval($bst);
  1682. $rt = $rt.",".$brt;
  1683. return "$name|$rowlist|$device|$reading|$rt|$rowsrej|$ncpslist|0";
  1684. }
  1685. ####################################################################################################
  1686. # Auswertungsroutine der nichtblockierenden DB-Abfrage diffValue
  1687. ####################################################################################################
  1688. sub diffval_ParseDone($) {
  1689. my ($string) = @_;
  1690. my @a = split("\\|",$string);
  1691. my $hash = $defs{$a[0]};
  1692. my $name = $hash->{NAME};
  1693. my $rowlist = decode_base64($a[1]);
  1694. my $device = $a[2];
  1695. $device =~ s/%/\//g;
  1696. my $reading = $a[3];
  1697. $reading =~ s/%/\//g;
  1698. my $bt = $a[4];
  1699. my ($rt,$brt) = split(",", $bt);
  1700. my $rowsrej = $a[5]?decode_base64($a[5]):undef; # String von Datensätzen die nicht berücksichtigt wurden (diff Schwellenwert Überschreitung)
  1701. my $ncpslist = decode_base64($a[6]); # Hash von Perioden die nicht kalkuliert werden konnten "no calc in period"
  1702. my $err = $a[7]?decode_base64($a[7]):undef;
  1703. my $reading_runtime_string;
  1704. my $difflimit = AttrVal($name, "diffAccept", "20"); # legt fest, bis zu welchem Wert Differenzen akzeptoert werden (Ausreißer eliminieren)AttrVal($name, "diffAccept", "20");
  1705. Log3 ($name, 4, "DbRep $name -> Start BlockingCall diffval_ParseDone");
  1706. if ($err) {
  1707. readingsSingleUpdate($hash, "errortext", $err, 1);
  1708. readingsSingleUpdate($hash, "state", "error", 1);
  1709. delete($hash->{HELPER}{RUNNING_PID});
  1710. Log3 ($name, 4, "DbRep $name -> BlockingCall diffval_ParseDone finished");
  1711. return;
  1712. }
  1713. # only for this block because of warnings if details of readings are not set
  1714. no warnings 'uninitialized';
  1715. # Auswertung hashes für state-Warning
  1716. $rowsrej =~ s/_/ /g;
  1717. Log3 ($name, 3, "DbRep $name -> data ignored while calc diffValue due to threshold overrun (diffAccept = $difflimit): \n$rowsrej")
  1718. if($rowsrej);
  1719. $rowsrej =~ s/\n/ \|\| /g;
  1720. my %ncp = split("§", $ncpslist);
  1721. my $ncpstr;
  1722. if(%ncp) {
  1723. foreach my $ncpkey (sort(keys(%ncp))) {
  1724. $ncpstr .= $ncpkey." || ";
  1725. }
  1726. }
  1727. # Readingaufbereitung
  1728. my %rh = split("§", $rowlist);
  1729. Log3 ($name, 4, "DbRep $name - result of diffValue calculation after decoding:");
  1730. foreach my $key (sort(keys(%rh))) {
  1731. Log3 ($name, 4, "DbRep $name - runtimestring Key: $key, value: ".$rh{$key});
  1732. }
  1733. readingsBeginUpdate($hash);
  1734. foreach my $key (sort(keys(%rh))) {
  1735. my @k = split("\\|",$rh{$key});
  1736. my $rts = $k[2]."__";
  1737. $rts =~ s/:/-/g; # substituieren unsupported characters -> siehe fhem.pl
  1738. if (AttrVal($hash->{NAME}, "readingNameMap", "")) {
  1739. $reading_runtime_string = $rts.AttrVal($hash->{NAME}, "readingNameMap", "")."__".$k[0];
  1740. } else {
  1741. my $ds = $device."__" if ($device);
  1742. my $rds = $reading."__" if ($reading);
  1743. $reading_runtime_string = $rts.$ds.$rds."DIFF__".$k[0];
  1744. }
  1745. my $rv = $k[1];
  1746. readingsBulkUpdate($hash, $reading_runtime_string, $rv?sprintf("%.4f",$rv):"-");
  1747. }
  1748. readingsBulkUpdate($hash, "background_processing_time", sprintf("%.4f",$brt)) if(AttrVal($name, "showproctime", undef));
  1749. readingsBulkUpdate($hash, "sql_processing_time", sprintf("%.4f",$rt)) if(AttrVal($name, "showproctime", undef));
  1750. readingsBulkUpdate($hash, "diff_overrun_limit_".$difflimit, $rowsrej) if($rowsrej);
  1751. readingsBulkUpdate($hash, "less_data_in_period", $ncpstr) if($ncpstr);
  1752. readingsBulkUpdate($hash, "state", ($ncpstr||$rowsrej)?"Warning":"done");
  1753. readingsEndUpdate($hash, 1);
  1754. delete($hash->{HELPER}{RUNNING_PID});
  1755. Log3 ($name, 4, "DbRep $name -> BlockingCall diffval_ParseDone finished");
  1756. return;
  1757. }
  1758. ####################################################################################################
  1759. # nichtblockierende DB-Abfrage sumValue
  1760. ####################################################################################################
  1761. sub sumval_DoParse($) {
  1762. my ($string) = @_;
  1763. my ($name, $device, $reading, $ts) = split("\\§", $string);
  1764. my $hash = $defs{$name};
  1765. my $dbloghash = $hash->{dbloghash};
  1766. my $dbconn = $dbloghash->{dbconn};
  1767. my $dbuser = $dbloghash->{dbuser};
  1768. my $dblogname = $dbloghash->{NAME};
  1769. my $dbpassword = $attr{"sec$dblogname"}{secret};
  1770. my $err;
  1771. # Background-Startzeit
  1772. my $bst = [gettimeofday];
  1773. Log3 ($name, 4, "DbRep $name -> Start BlockingCall sumval_DoParse");
  1774. my $dbh;
  1775. eval {$dbh = DBI->connect("dbi:$dbconn", $dbuser, $dbpassword, { PrintError => 0, RaiseError => 1, AutoInactiveDestroy => 1 });};
  1776. if ($@) {
  1777. $err = encode_base64($@,"");
  1778. Log3 ($name, 2, "DbRep $name - $@");
  1779. Log3 ($name, 4, "DbRep $name -> BlockingCall sumval_DoParse finished");
  1780. return "$name|''|$device|$reading|''|$err";
  1781. }
  1782. # only for this block because of warnings if details of readings are not set
  1783. no warnings 'uninitialized';
  1784. # Timestampstring to Array
  1785. my @ts = split("\\|", $ts);
  1786. Log3 ($name, 5, "DbRep $name - Timestamp-Array: \n@ts");
  1787. # SQL-Startzeit
  1788. my $st = [gettimeofday];
  1789. # DB-Abfrage zeilenweise für jeden Array-Eintrag
  1790. my $arrstr;
  1791. foreach my $row (@ts) {
  1792. my @a = split("#", $row);
  1793. my $runtime_string = $a[0];
  1794. my $runtime_string_first = $a[1];
  1795. my $runtime_string_next = $a[2];
  1796. # SQL zusammenstellen für DB-Abfrage
  1797. my $sql = "SELECT SUM(VALUE) FROM `history` where ";
  1798. $sql .= "DEVICE LIKE '$device' AND " if($device);
  1799. $sql .= "READING LIKE '$reading' AND " if($reading);
  1800. $sql .= "TIMESTAMP >= '$runtime_string_first' AND TIMESTAMP < '$runtime_string_next' ;";
  1801. Log3 ($name, 4, "DbRep $name - SQL execute: $sql");
  1802. my $line;
  1803. # DB-Abfrage -> Ergebnis in $arrstr aufnehmen
  1804. eval {$line = $dbh->selectrow_array($sql);};
  1805. if ($@) {
  1806. $err = encode_base64($@,"");
  1807. Log3 ($name, 2, "DbRep $name - $@");
  1808. $dbh->disconnect;
  1809. Log3 ($name, 4, "DbRep $name -> BlockingCall sumval_DoParse finished");
  1810. return "$name|''|$device|$reading|''|$err";
  1811. } else {
  1812. Log3($name, 5, "DbRep $name - SQL result: $line") if($line);
  1813. if(AttrVal($name, "aggregation", "") eq "hour") {
  1814. my @rsf = split(/[" "\|":"]/,$runtime_string_first);
  1815. $arrstr .= $runtime_string."#".$line."#".$rsf[0]."_".$rsf[1]."|";
  1816. } else {
  1817. my @rsf = split(" ",$runtime_string_first);
  1818. $arrstr .= $runtime_string."#".$line."#".$rsf[0]."|";
  1819. }
  1820. }
  1821. }
  1822. # SQL-Laufzeit ermitteln
  1823. my $rt = tv_interval($st);
  1824. $dbh->disconnect;
  1825. # Daten müssen als Einzeiler zurückgegeben werden
  1826. $arrstr = encode_base64($arrstr,"");
  1827. Log3 ($name, 4, "DbRep $name -> BlockingCall sumval_DoParse finished");
  1828. # Background-Laufzeit ermitteln
  1829. my $brt = tv_interval($bst);
  1830. $rt = $rt.",".$brt;
  1831. return "$name|$arrstr|$device|$reading|$rt|0";
  1832. }
  1833. ####################################################################################################
  1834. # Auswertungsroutine der nichtblockierenden DB-Abfrage sumValue
  1835. ####################################################################################################
  1836. sub sumval_ParseDone($) {
  1837. my ($string) = @_;
  1838. my @a = split("\\|",$string);
  1839. my $hash = $defs{$a[0]};
  1840. my $name = $hash->{NAME};
  1841. my $arrstr = decode_base64($a[1]);
  1842. my $device = $a[2];
  1843. $device =~ s/%/\//g;
  1844. my $reading = $a[3];
  1845. $reading =~ s/%/\//g;
  1846. my $bt = $a[4];
  1847. my ($rt,$brt) = split(",", $bt);
  1848. my $err = $a[5]?decode_base64($a[5]):undef;
  1849. my $reading_runtime_string;
  1850. Log3 ($name, 4, "DbRep $name -> Start BlockingCall sumval_ParseDone");
  1851. if ($err) {
  1852. readingsSingleUpdate($hash, "errortext", $err, 1);
  1853. readingsSingleUpdate($hash, "state", "error", 1);
  1854. delete($hash->{HELPER}{RUNNING_PID});
  1855. Log3 ($name, 4, "DbRep $name -> BlockingCall sumval_ParseDone finished");
  1856. return;
  1857. }
  1858. # only for this block because of warnings if details of readings are not set
  1859. no warnings 'uninitialized';
  1860. # Readingaufbereitung
  1861. readingsBeginUpdate($hash);
  1862. my @arr = split("\\|", $arrstr);
  1863. foreach my $row (@arr) {
  1864. my @a = split("#", $row);
  1865. my $runtime_string = $a[0];
  1866. my $c = $a[1];
  1867. my $rsf = $a[2]."__";
  1868. if (AttrVal($hash->{NAME}, "readingNameMap", "")) {
  1869. $reading_runtime_string = $rsf.AttrVal($hash->{NAME}, "readingNameMap", "")."__".$runtime_string;
  1870. } else {
  1871. my $ds = $device."__" if ($device);
  1872. my $rds = $reading."__" if ($reading);
  1873. $reading_runtime_string = $rsf.$ds.$rds."SUM__".$runtime_string;
  1874. }
  1875. readingsBulkUpdate($hash, $reading_runtime_string, $c?sprintf("%.4f",$c):"-");
  1876. }
  1877. readingsBulkUpdate($hash, "background_processing_time", sprintf("%.4f",$brt)) if(AttrVal($name, "showproctime", undef));
  1878. readingsBulkUpdate($hash, "sql_processing_time", sprintf("%.4f",$rt)) if(AttrVal($name, "showproctime", undef));
  1879. readingsBulkUpdate($hash, "state", "done");
  1880. readingsEndUpdate($hash, 1);
  1881. delete($hash->{HELPER}{RUNNING_PID});
  1882. Log3 ($name, 4, "DbRep $name -> BlockingCall sumval_ParseDone finished");
  1883. return;
  1884. }
  1885. ####################################################################################################
  1886. # nichtblockierendes DB delete
  1887. ####################################################################################################
  1888. sub del_DoParse($) {
  1889. my ($string) = @_;
  1890. my ($name, $device, $reading, $runtime_string_first, $runtime_string_next) = split("\\|", $string);
  1891. my $hash = $defs{$name};
  1892. my $dbloghash = $hash->{dbloghash};
  1893. my $dbconn = $dbloghash->{dbconn};
  1894. my $dbuser = $dbloghash->{dbuser};
  1895. my $dblogname = $dbloghash->{NAME};
  1896. my $dbpassword = $attr{"sec$dblogname"}{secret};
  1897. my $err;
  1898. # Background-Startzeit
  1899. my $bst = [gettimeofday];
  1900. Log3 ($name, 4, "DbRep $name -> Start BlockingCall del_DoParse");
  1901. my $dbh;
  1902. eval {$dbh = DBI->connect("dbi:$dbconn", $dbuser, $dbpassword, { PrintError => 0, RaiseError => 1, AutoCommit => 1, AutoInactiveDestroy => 1 });};
  1903. if ($@) {
  1904. $err = encode_base64($@,"");
  1905. Log3 ($name, 2, "DbRep $name - $@");
  1906. Log3 ($name, 4, "DbRep $name -> BlockingCall del_DoParse finished");
  1907. return "$name|''|''|$err";
  1908. }
  1909. # SQL zusammenstellen für DB-Operation
  1910. my $sql = "DELETE FROM history where ";
  1911. $sql .= "DEVICE LIKE '$device' AND " if($device);
  1912. $sql .= "READING LIKE '$reading' AND " if($reading);
  1913. $sql .= "TIMESTAMP >= ? AND TIMESTAMP < ? ;";
  1914. # SQL zusammenstellen für Logausgabe
  1915. my $sql1 = "DELETE FROM history where ";
  1916. $sql1 .= "DEVICE LIKE '$device' AND " if($device);
  1917. $sql1 .= "READING LIKE '$reading' AND " if($reading);
  1918. $sql1 .= "TIMESTAMP >= '$runtime_string_first' AND TIMESTAMP < '$runtime_string_next';";
  1919. Log3 ($name, 4, "DbRep $name - SQL execute: $sql1");
  1920. # SQL-Startzeit
  1921. my $st = [gettimeofday];
  1922. my $sth = $dbh->prepare($sql);
  1923. eval {$sth->execute($runtime_string_first, $runtime_string_next);};
  1924. my $rows;
  1925. if ($@) {
  1926. $err = encode_base64($@,"");
  1927. Log3 ($name, 2, "DbRep $name - $@");
  1928. $dbh->disconnect;
  1929. Log3 ($name, 4, "DbRep $name -> BlockingCall del_DoParse finished");
  1930. return "$name|''|''|$err";
  1931. } else {
  1932. $rows = $sth->rows;
  1933. $dbh->commit() if(!$dbh->{AutoCommit});
  1934. $dbh->disconnect;
  1935. }
  1936. # SQL-Laufzeit ermitteln
  1937. my $rt = tv_interval($st);
  1938. Log3 ($name, 5, "DbRep $name -> Number of deleted rows: $rows");
  1939. Log3 ($name, 4, "DbRep $name -> BlockingCall del_DoParse finished");
  1940. # Background-Laufzeit ermitteln
  1941. my $brt = tv_interval($bst);
  1942. $rt = $rt.",".$brt;
  1943. return "$name|$rows|$rt|0";
  1944. }
  1945. ####################################################################################################
  1946. # Auswertungsroutine DB delete
  1947. ####################################################################################################
  1948. sub del_ParseDone($) {
  1949. my ($string) = @_;
  1950. my @a = split("\\|",$string);
  1951. my $hash = $defs{$a[0]};
  1952. my $name = $hash->{NAME};
  1953. my $rows = $a[1];
  1954. my $bt = $a[2];
  1955. my ($rt,$brt) = split(",", $bt);
  1956. my $err = $a[3]?decode_base64($a[3]):undef;
  1957. Log3 ($name, 4, "DbRep $name -> Start BlockingCall del_ParseDone");
  1958. if ($err) {
  1959. readingsSingleUpdate($hash, "errortext", $err, 1);
  1960. readingsSingleUpdate($hash, "state", "error", 1);
  1961. delete($hash->{HELPER}{RUNNING_PID});
  1962. Log3 ($name, 4, "DbRep $name -> BlockingCall del_ParseDone finished");
  1963. return;
  1964. }
  1965. my $reading = AttrVal($hash->{NAME}, "reading", undef);
  1966. $reading =~ s/%/\//g if ($reading);
  1967. my $device = AttrVal($hash->{NAME}, "device", undef);
  1968. $device =~ s/%/\//g if ($device);
  1969. # only for this block because of warnings if details of readings are not set
  1970. no warnings 'uninitialized';
  1971. my ($reading_runtime_string, $ds, $rds);
  1972. if (AttrVal($hash->{NAME}, "readingNameMap", "")) {
  1973. $reading_runtime_string = AttrVal($hash->{NAME}, "readingNameMap", "")." -- DELETED ROWS -- ";
  1974. } else {
  1975. $ds = $device." -- " if ($device);
  1976. $rds = $reading." -- " if ($reading);
  1977. $reading_runtime_string = $ds.$rds." -- DELETED ROWS -- ";
  1978. }
  1979. readingsBeginUpdate($hash);
  1980. readingsBulkUpdate($hash, $reading_runtime_string, $rows);
  1981. $rows = $ds.$rds.$rows;
  1982. Log3 ($name, 3, "DbRep $name - Entries of database $hash->{DATABASE} deleted: $rows");
  1983. readingsBulkUpdate($hash, "background_processing_time", sprintf("%.4f",$brt)) if(AttrVal($name, "showproctime", undef));
  1984. readingsBulkUpdate($hash, "sql_processing_time", sprintf("%.4f",$rt)) if(AttrVal($name, "showproctime", undef));
  1985. readingsBulkUpdate($hash, "state", "done");
  1986. readingsEndUpdate($hash, 1);
  1987. delete($hash->{HELPER}{RUNNING_PID});
  1988. Log3 ($name, 4, "DbRep $name -> BlockingCall del_ParseDone finished");
  1989. return;
  1990. }
  1991. ####################################################################################################
  1992. # nichtblockierendes DB insert
  1993. ####################################################################################################
  1994. sub insert_Push($) {
  1995. my ($name) = @_;
  1996. my $hash = $defs{$name};
  1997. my $dbloghash = $hash->{dbloghash};
  1998. my $dbconn = $dbloghash->{dbconn};
  1999. my $dbuser = $dbloghash->{dbuser};
  2000. my $dblogname = $dbloghash->{NAME};
  2001. my $dbpassword = $attr{"sec$dblogname"}{secret};
  2002. my $err;
  2003. # Background-Startzeit
  2004. my $bst = [gettimeofday];
  2005. Log3 ($name, 4, "DbRep $name -> Start BlockingCall insert_Push");
  2006. my $dbh;
  2007. eval {$dbh = DBI->connect("dbi:$dbconn", $dbuser, $dbpassword, { PrintError => 0, RaiseError => 1, AutoCommit => 1, AutoInactiveDestroy => 1 });};
  2008. if ($@) {
  2009. $err = encode_base64($@,"");
  2010. Log3 ($name, 2, "DbRep $name - $@");
  2011. Log3 ($name, 4, "DbRep $name -> BlockingCall insert_Push finished");
  2012. return "$name|''|''|$err";
  2013. }
  2014. my $i_timestamp = $hash->{HELPER}{I_TIMESTAMP};
  2015. my $i_device = $hash->{HELPER}{I_DEVICE};
  2016. my $i_type = $hash->{HELPER}{I_TYPE};
  2017. my $i_event = $hash->{HELPER}{I_EVENT};
  2018. my $i_reading = $hash->{HELPER}{I_READING};
  2019. my $i_value = $hash->{HELPER}{I_VALUE};
  2020. my $i_unit = $hash->{HELPER}{I_UNIT} ? $hash->{HELPER}{I_UNIT} : " ";
  2021. # SQL zusammenstellen für DB-Operation
  2022. Log3 ($name, 5, "DbRep $name -> data to insert Timestamp: $i_timestamp, Device: $i_device, Type: $i_type, Event: $i_event, Reading: $i_reading, Value: $i_value, Unit: $i_unit");
  2023. # SQL-Startzeit
  2024. my $st = [gettimeofday];
  2025. $dbh->begin_work();
  2026. my $sth = $dbh->prepare_cached("INSERT INTO history (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES (?,?,?,?,?,?,?)");
  2027. eval {$sth->execute($i_timestamp, $i_device, $i_type, $i_event, $i_reading, $i_value, $i_unit);};
  2028. my $irow;
  2029. if ($@) {
  2030. $err = encode_base64($@,"");
  2031. Log3 ($name, 2, "DbRep $name - Failed to insert new dataset into database: $@");
  2032. $dbh->rollback();
  2033. $dbh->disconnect();
  2034. Log3 ($name, 4, "DbRep $name -> BlockingCall insert_Push finished");
  2035. return "$name|''|''|$err";
  2036. } else {
  2037. $dbh->commit();
  2038. $irow = $sth->rows;
  2039. $dbh->disconnect();
  2040. }
  2041. # SQL-Laufzeit ermitteln
  2042. my $rt = tv_interval($st);
  2043. Log3 ($name, 4, "DbRep $name -> BlockingCall insert_Push finished");
  2044. # Background-Laufzeit ermitteln
  2045. my $brt = tv_interval($bst);
  2046. $rt = $rt.",".$brt;
  2047. return "$name|$irow|$rt|0";
  2048. }
  2049. ####################################################################################################
  2050. # Auswertungsroutine DB insert
  2051. ####################################################################################################
  2052. sub insert_Done($) {
  2053. my ($string) = @_;
  2054. my @a = split("\\|",$string);
  2055. my $hash = $defs{$a[0]};
  2056. my $name = $hash->{NAME};
  2057. my $irow = $a[1];
  2058. my $bt = $a[2];
  2059. my ($rt,$brt) = split(",", $bt);
  2060. my $err = $a[3]?decode_base64($a[3]):undef;
  2061. Log3 ($name, 4, "DbRep $name -> Start BlockingCall insert_Done");
  2062. my $i_timestamp = delete $hash->{HELPER}{I_TIMESTAMP};
  2063. my $i_device = delete $hash->{HELPER}{I_DEVICE};
  2064. my $i_type = delete $hash->{HELPER}{I_TYPE};
  2065. my $i_event = delete $hash->{HELPER}{I_EVENT};
  2066. my $i_reading = delete $hash->{HELPER}{I_READING};
  2067. my $i_value = delete $hash->{HELPER}{I_VALUE};
  2068. my $i_unit = delete $hash->{HELPER}{I_UNIT};
  2069. if ($err) {
  2070. readingsSingleUpdate($hash, "errortext", $err, 1);
  2071. readingsSingleUpdate($hash, "state", "error", 1);
  2072. delete($hash->{HELPER}{RUNNING_PID});
  2073. Log3 ($name, 4, "DbRep $name -> BlockingCall insert_Done finished");
  2074. return;
  2075. }
  2076. # only for this block because of warnings if details of readings are not set
  2077. no warnings 'uninitialized';
  2078. readingsBeginUpdate($hash);
  2079. readingsBulkUpdate($hash, "number_lines_inserted", $irow);
  2080. readingsBulkUpdate($hash, "data_inserted", $i_timestamp.", ".$i_device.", ".$i_type.", ".$i_event.", ".$i_reading.", ".$i_value.", ".$i_unit);
  2081. readingsBulkUpdate($hash, "background_processing_time", sprintf("%.4f",$brt)) if(AttrVal($name, "showproctime", undef));
  2082. readingsBulkUpdate($hash, "sql_processing_time", sprintf("%.4f",$rt)) if(AttrVal($name, "showproctime", undef));
  2083. readingsBulkUpdate($hash, "state", "done");
  2084. readingsEndUpdate($hash, 1);
  2085. Log3 ($name, 5, "DbRep $name - Inserted into database $hash->{DATABASE} table 'history': Timestamp: $i_timestamp, Device: $i_device, Type: $i_type, Event: $i_event, Reading: $i_reading, Value: $i_value, Unit: $i_unit");
  2086. delete($hash->{HELPER}{RUNNING_PID});
  2087. Log3 ($name, 4, "DbRep $name -> BlockingCall insert_Done finished");
  2088. return;
  2089. }
  2090. ####################################################################################################
  2091. # nichtblockierendes DB deviceRename / readingRename
  2092. ####################################################################################################
  2093. sub devren_Push($) {
  2094. my ($name) = @_;
  2095. my $hash = $defs{$name};
  2096. my $dbloghash = $hash->{dbloghash};
  2097. my $dbconn = $dbloghash->{dbconn};
  2098. my $dbuser = $dbloghash->{dbuser};
  2099. my $dblogname = $dbloghash->{NAME};
  2100. my $dbpassword = $attr{"sec$dblogname"}{secret};
  2101. my $err;
  2102. # Background-Startzeit
  2103. my $bst = [gettimeofday];
  2104. Log3 ($name, 4, "DbRep $name -> Start BlockingCall devren_Push");
  2105. my $dbh;
  2106. eval {$dbh = DBI->connect("dbi:$dbconn", $dbuser, $dbpassword, { PrintError => 0, RaiseError => 1, AutoCommit => 1, AutoInactiveDestroy => 1 });};
  2107. if ($@) {
  2108. $err = encode_base64($@,"");
  2109. Log3 ($name, 2, "DbRep $name - $@");
  2110. Log3 ($name, 4, "DbRep $name -> BlockingCall devren_Push finished");
  2111. return "$name|''|''|$err";
  2112. }
  2113. my $renmode = $hash->{HELPER}{RENMODE};
  2114. # SQL-Startzeit
  2115. my $st = [gettimeofday];
  2116. my ($sth,$old,$new);
  2117. $dbh->begin_work();
  2118. if ($renmode eq "devren") {
  2119. $old = delete $hash->{HELPER}{OLDDEV};
  2120. $new = delete $hash->{HELPER}{NEWDEV};
  2121. # SQL zusammenstellen für DB-Operation
  2122. Log3 ($name, 5, "DbRep $name -> Rename old device name \"$old\" to new device name \"$new\" in database $dblogname ");
  2123. # prepare DB operation
  2124. $sth = $dbh->prepare_cached("UPDATE history SET TIMESTAMP=TIMESTAMP,DEVICE=? WHERE DEVICE=? ") ;
  2125. } elsif ($renmode eq "readren") {
  2126. $old = delete $hash->{HELPER}{OLDREAD};
  2127. $new = delete $hash->{HELPER}{NEWREAD};
  2128. # SQL zusammenstellen für DB-Operation
  2129. Log3 ($name, 5, "DbRep $name -> Rename old reading name \"$old\" to new reading name \"$new\" in database $dblogname ");
  2130. # prepare DB operation
  2131. $sth = $dbh->prepare_cached("UPDATE history SET TIMESTAMP=TIMESTAMP,READING=? WHERE READING=? ") ;
  2132. }
  2133. eval {$sth->execute($new, $old);};
  2134. my $urow;
  2135. if ($@) {
  2136. $err = encode_base64($@,"");
  2137. Log3 ($name, 2, "DbRep $name - Failed to rename old device name \"$old\" to new device name \"$new\": $@") if($renmode eq "devren");
  2138. Log3 ($name, 2, "DbRep $name - Failed to rename old reading name \"$old\" to new reading name \"$new\": $@") if($renmode eq "readren");
  2139. $dbh->rollback();
  2140. $dbh->disconnect();
  2141. Log3 ($name, 4, "DbRep $name -> BlockingCall devren_Push finished");
  2142. return "$name|''|''|$err";
  2143. } else {
  2144. $dbh->commit();
  2145. $urow = $sth->rows;
  2146. $dbh->disconnect();
  2147. }
  2148. # SQL-Laufzeit ermitteln
  2149. my $rt = tv_interval($st);
  2150. Log3 ($name, 4, "DbRep $name -> BlockingCall devren_Push finished");
  2151. # Background-Laufzeit ermitteln
  2152. my $brt = tv_interval($bst);
  2153. $rt = $rt.",".$brt;
  2154. return "$name|$urow|$rt|0|$old|$new";
  2155. }
  2156. ####################################################################################################
  2157. # Auswertungsroutine DB deviceRename
  2158. ####################################################################################################
  2159. sub devren_Done($) {
  2160. my ($string) = @_;
  2161. my @a = split("\\|",$string);
  2162. my $hash = $defs{$a[0]};
  2163. my $name = $hash->{NAME};
  2164. my $urow = $a[1];
  2165. my $bt = $a[2];
  2166. my ($rt,$brt) = split(",", $bt);
  2167. my $err = $a[3]?decode_base64($a[3]):undef;
  2168. my $old = $a[4];
  2169. my $new = $a[5];
  2170. Log3 ($name, 4, "DbRep $name -> Start BlockingCall devren_Done");
  2171. my $renmode = delete $hash->{HELPER}{RENMODE};
  2172. if ($err) {
  2173. readingsSingleUpdate($hash, "errortext", $err, 1);
  2174. readingsSingleUpdate($hash, "state", "error", 1);
  2175. delete($hash->{HELPER}{RUNNING_PID});
  2176. Log3 ($name, 4, "DbRep $name -> BlockingCall devren_Done finished");
  2177. return;
  2178. }
  2179. # only for this block because of warnings if details of readings are not set
  2180. no warnings 'uninitialized';
  2181. readingsBeginUpdate($hash);
  2182. readingsBulkUpdate($hash, "number_lines_updated", $urow);
  2183. if($renmode eq "devren") {
  2184. readingsBulkUpdate($hash, "device_renamed", "old: ".$old." to new: ".$new) if($urow != 0);
  2185. readingsBulkUpdate($hash, "device_not_renamed", "WARNING - old: ".$old." not found, not renamed to new: ".$new) if($urow == 0);
  2186. }
  2187. if($renmode eq "readren") {
  2188. readingsBulkUpdate($hash, "reading_renamed", "old: ".$old." to new: ".$new) if($urow != 0);
  2189. readingsBulkUpdate($hash, "reading_not_renamed", "WARNING - old: ".$old." not found, not renamed to new: ".$new) if ($urow == 0);
  2190. }
  2191. readingsBulkUpdate($hash, "background_processing_time", sprintf("%.4f",$brt)) if(AttrVal($name, "showproctime", undef));
  2192. readingsBulkUpdate($hash, "sql_processing_time", sprintf("%.4f",$rt)) if(AttrVal($name, "showproctime", undef));
  2193. readingsBulkUpdate($hash, "state", "done");
  2194. readingsEndUpdate($hash, 1);
  2195. if ($urow != 0) {
  2196. Log3 ($name, 3, "DbRep ".(($hash->{ROLE} eq "Agent")?"Agent ":"")."$name - DEVICE renamed in \"$hash->{DATABASE}\", old: \"$old\", new: \"$new\", amount: $urow ") if($renmode eq "devren");
  2197. Log3 ($name, 3, "DbRep ".(($hash->{ROLE} eq "Agent")?"Agent ":"")."$name - READING renamed in \"$hash->{DATABASE}\", old: \"$old\", new: \"$new\", amount: $urow ") if($renmode eq "readren");
  2198. } else {
  2199. Log3 ($name, 3, "DbRep ".(($hash->{ROLE} eq "Agent")?"Agent ":"")."$name - WARNING - old device \"$old\" was not found in database \"$hash->{DATABASE}\" ") if($renmode eq "devren");
  2200. Log3 ($name, 3, "DbRep ".(($hash->{ROLE} eq "Agent")?"Agent ":"")."$name - WARNING - old reading \"$old\" was not found in database \"$hash->{DATABASE}\" ") if($renmode eq "readren");
  2201. }
  2202. delete($hash->{HELPER}{RUNNING_PID});
  2203. Log3 ($name, 4, "DbRep $name -> BlockingCall devren_Done finished");
  2204. return;
  2205. }
  2206. ####################################################################################################
  2207. # nichtblockierende DB-Abfrage fetchrows
  2208. ####################################################################################################
  2209. sub fetchrows_DoParse($) {
  2210. my ($string) = @_;
  2211. my ($name, $device, $reading, $runtime_string_first, $runtime_string_next) = split("\\|", $string);
  2212. my $hash = $defs{$name};
  2213. my $dbloghash = $hash->{dbloghash};
  2214. my $dbconn = $dbloghash->{dbconn};
  2215. my $dbuser = $dbloghash->{dbuser};
  2216. my $dblogname = $dbloghash->{NAME};
  2217. my $dbpassword = $attr{"sec$dblogname"}{secret};
  2218. my $err;
  2219. # Background-Startzeit
  2220. my $bst = [gettimeofday];
  2221. Log3 ($name, 4, "DbRep $name -> Start BlockingCall fetchrows_DoParse");
  2222. my $dbh;
  2223. eval {$dbh = DBI->connect("dbi:$dbconn", $dbuser, $dbpassword, { PrintError => 0, RaiseError => 1, AutoInactiveDestroy => 1 });};
  2224. if ($@) {
  2225. $err = encode_base64($@,"");
  2226. Log3 ($name, 2, "DbRep $name - $@");
  2227. Log3 ($name, 4, "DbRep $name -> BlockingCall fetchrows_DoParse finished");
  2228. return "$name|''|''|$err";
  2229. }
  2230. # SQL zusammenstellen
  2231. my $sql = "SELECT DEVICE,READING,TIMESTAMP,VALUE FROM history where ";
  2232. $sql .= "DEVICE LIKE '$device' AND " if($device);
  2233. $sql .= "READING LIKE '$reading' AND " if($reading);
  2234. $sql .= "TIMESTAMP >= ? AND TIMESTAMP < ? ORDER BY TIMESTAMP ;";
  2235. # SQL zusammenstellen für Logfileausgabe
  2236. my $sql1 = "SELECT DEVICE,READING,TIMESTAMP,VALUE FROM history where ";
  2237. $sql1 .= "DEVICE LIKE '$device' AND " if($device);
  2238. $sql1 .= "READING LIKE '$reading' AND " if($reading);
  2239. $sql1 .= "TIMESTAMP >= '$runtime_string_first' AND TIMESTAMP < '$runtime_string_next' ORDER BY TIMESTAMP;";
  2240. Log3 ($name, 4, "DbRep $name - SQL execute: $sql1");
  2241. # SQL-Startzeit
  2242. my $st = [gettimeofday];
  2243. my $sth = $dbh->prepare($sql);
  2244. eval {$sth->execute($runtime_string_first, $runtime_string_next);};
  2245. my $rowlist;
  2246. if ($@) {
  2247. $err = encode_base64($@,"");
  2248. Log3 ($name, 2, "DbRep $name - $@");
  2249. $dbh->disconnect;
  2250. Log3 ($name, 4, "DbRep $name -> BlockingCall fetchrows_DoParse finished");
  2251. return "$name|''|''|$err";
  2252. } else {
  2253. my @row_array = map { $_ -> [0]." ".$_ -> [1]." ".$_ -> [2]." ".$_ -> [3]."\n" } @{$sth->fetchall_arrayref()};
  2254. $rowlist = join('|', @row_array);
  2255. Log3 ($name, 5, "DbRep $name -> row_array: @row_array");
  2256. }
  2257. # SQL-Laufzeit ermitteln
  2258. my $rt = tv_interval($st);
  2259. $dbh->disconnect;
  2260. # Daten müssen als Einzeiler zurückgegeben werden
  2261. $rowlist = encode_base64($rowlist,"");
  2262. Log3 ($name, 4, "DbRep $name -> BlockingCall fetchrows_DoParse finished");
  2263. # Background-Laufzeit ermitteln
  2264. my $brt = tv_interval($bst);
  2265. $rt = $rt.",".$brt;
  2266. return "$name|$rowlist|$rt|0";
  2267. }
  2268. ####################################################################################################
  2269. # Auswertungsroutine der nichtblockierenden DB-Abfrage fetchrows
  2270. ####################################################################################################
  2271. sub fetchrows_ParseDone($) {
  2272. my ($string) = @_;
  2273. my @a = split("\\|",$string);
  2274. my $hash = $defs{$a[0]};
  2275. my $rowlist = decode_base64($a[1]);
  2276. my $bt = $a[2];
  2277. my ($rt,$brt) = split(",", $bt);
  2278. my $err = $a[3]?decode_base64($a[3]):undef;
  2279. my $name = $hash->{NAME};
  2280. my $reading = AttrVal($name, "reading", undef);
  2281. my @i;
  2282. my @row;
  2283. my $reading_runtime_string;
  2284. Log3 ($name, 4, "DbRep $name -> Start BlockingCall fetchrows_ParseDone");
  2285. if ($err) {
  2286. readingsSingleUpdate($hash, "errortext", $err, 1);
  2287. readingsSingleUpdate($hash, "state", "error", 1);
  2288. delete($hash->{HELPER}{RUNNING_PID});
  2289. Log3 ($name, 4, "DbRep $name -> BlockingCall fetchrows_ParseDone finished");
  2290. return;
  2291. }
  2292. my @row_array = split("\\|", $rowlist);
  2293. Log3 ($name, 5, "DbRep $name - row_array decoded: @row_array");
  2294. # Readingaufbereitung
  2295. readingsBeginUpdate($hash);
  2296. foreach my $row (@row_array) {
  2297. my @a = split("[ \t][ \t]*", $row, 5);
  2298. my $dev = $a[0];
  2299. my $rea = $a[1];
  2300. $a[3] =~ s/:/-/g; # substituieren unsopported characters ":" -> siehe fhem.pl
  2301. my $ts = $a[2]."_".$a[3];
  2302. my $val = $a[4];
  2303. if ($reading && AttrVal($hash->{NAME}, "readingNameMap", "")) {
  2304. $reading_runtime_string = $ts."__".AttrVal($hash->{NAME}, "readingNameMap", "") ;
  2305. } else {
  2306. $reading_runtime_string = $ts."__".$dev."__".$rea;
  2307. }
  2308. readingsBulkUpdate($hash, $reading_runtime_string, $val);
  2309. }
  2310. readingsBulkUpdate($hash, "background_processing_time", sprintf("%.4f",$brt)) if(AttrVal($name, "showproctime", undef));
  2311. readingsBulkUpdate($hash, "sql_processing_time", sprintf("%.4f",$rt)) if(AttrVal($name, "showproctime", undef));
  2312. readingsBulkUpdate($hash, "state", "done");
  2313. readingsEndUpdate($hash, 1);
  2314. delete($hash->{HELPER}{RUNNING_PID});
  2315. Log3 ($name, 4, "DbRep $name -> BlockingCall fetchrows_ParseDone finished");
  2316. return;
  2317. }
  2318. ####################################################################################################
  2319. # nichtblockierende DB-Funktion expfile
  2320. ####################################################################################################
  2321. sub expfile_DoParse($) {
  2322. my ($string) = @_;
  2323. my ($name, $device, $reading, $runtime_string_first, $runtime_string_next) = split("\\|", $string);
  2324. my $hash = $defs{$name};
  2325. my $dbloghash = $hash->{dbloghash};
  2326. my $dbconn = $dbloghash->{dbconn};
  2327. my $dbuser = $dbloghash->{dbuser};
  2328. my $dblogname = $dbloghash->{NAME};
  2329. my $dbpassword = $attr{"sec$dblogname"}{secret};
  2330. my $err=0;
  2331. # Background-Startzeit
  2332. my $bst = [gettimeofday];
  2333. Log3 ($name, 4, "DbRep $name -> Start BlockingCall expfile_DoParse");
  2334. my $dbh;
  2335. eval {$dbh = DBI->connect("dbi:$dbconn", $dbuser, $dbpassword, { PrintError => 0, RaiseError => 1, AutoInactiveDestroy => 1 });};
  2336. if ($@) {
  2337. $err = encode_base64($@,"");
  2338. Log3 ($name, 2, "DbRep $name - $@");
  2339. Log3 ($name, 4, "DbRep $name -> BlockingCall expfile_DoParse finished");
  2340. return "$name|''|''|$err";
  2341. }
  2342. my $outfile = AttrVal($name, "expimpfile", undef);
  2343. if (open(FH, ">$outfile")) {
  2344. binmode (FH);
  2345. } else {
  2346. $err = encode_base64("could not open ".$outfile.": ".$!,"");
  2347. return "$name|''|''|$err";
  2348. }
  2349. # SQL zusammenstellen
  2350. my $sql = "SELECT TIMESTAMP,DEVICE,TYPE,EVENT,READING,VALUE,UNIT FROM history where ";
  2351. $sql .= "DEVICE LIKE '$device' AND " if($device);
  2352. $sql .= "READING LIKE '$reading' AND " if($reading);
  2353. $sql .= "TIMESTAMP >= ? AND TIMESTAMP < ? ORDER BY TIMESTAMP ;";
  2354. # SQL zusammenstellen für Logfileausgabe
  2355. my $sql1 = "SELECT TIMESTAMP,DEVICE,TYPE,EVENT,READING,VALUE,UNIT FROM FROM history where ";
  2356. $sql1 .= "DEVICE LIKE '$device' AND " if($device);
  2357. $sql1 .= "READING LIKE '$reading' AND " if($reading);
  2358. $sql1 .= "TIMESTAMP >= '$runtime_string_first' AND TIMESTAMP < '$runtime_string_next' ORDER BY TIMESTAMP;";
  2359. Log3 ($name, 4, "DbRep $name - SQL execute: $sql1");
  2360. # SQL-Startzeit
  2361. my $st = [gettimeofday];
  2362. my $sth = $dbh->prepare($sql);
  2363. eval {$sth->execute($runtime_string_first, $runtime_string_next);};
  2364. my $nrows = 0;
  2365. if ($@) {
  2366. $err = encode_base64($@,"");
  2367. Log3 ($name, 2, "DbRep $name - $@");
  2368. $dbh->disconnect;
  2369. Log3 ($name, 4, "DbRep $name -> BlockingCall expfile_DoParse finished");
  2370. return "$name|''|''|$err";
  2371. } else {
  2372. # only for this block because of warnings of uninitialized values
  2373. no warnings 'uninitialized';
  2374. while (my $row = $sth->fetchrow_arrayref) {
  2375. print FH join(',', map { s{"}{""}g; "\"$_\""; } @$row), "\n";
  2376. Log3 ($name, 5, "DbRep $name -> write row: @$row");
  2377. # Anzahl der Datensätze
  2378. $nrows++;
  2379. }
  2380. close(FH);
  2381. }
  2382. # SQL-Laufzeit ermitteln
  2383. my $rt = tv_interval($st);
  2384. $sth->finish;
  2385. $dbh->disconnect;
  2386. Log3 ($name, 4, "DbRep $name -> BlockingCall expfile_DoParse finished");
  2387. # Background-Laufzeit ermitteln
  2388. my $brt = tv_interval($bst);
  2389. $rt = $rt.",".$brt;
  2390. return "$name|$nrows|$rt|$err";
  2391. }
  2392. ####################################################################################################
  2393. # Auswertungsroutine der nichtblockierenden DB-Funktion expfile
  2394. ####################################################################################################
  2395. sub expfile_ParseDone($) {
  2396. my ($string) = @_;
  2397. my @a = split("\\|",$string);
  2398. my $hash = $defs{$a[0]};
  2399. my $nrows = $a[1];
  2400. my $bt = $a[2];
  2401. my ($rt,$brt) = split(",", $bt);
  2402. my $err = $a[3]?decode_base64($a[3]):undef;
  2403. my $name = $hash->{NAME};
  2404. Log3 ($name, 4, "DbRep $name -> Start BlockingCall expfile_ParseDone");
  2405. if ($err) {
  2406. readingsSingleUpdate($hash, "errortext", $err, 1);
  2407. readingsSingleUpdate($hash, "state", "error", 1);
  2408. delete($hash->{HELPER}{RUNNING_PID});
  2409. Log3 ($name, 4, "DbRep $name -> BlockingCall expfile_ParseDone finished");
  2410. return;
  2411. }
  2412. my $reading = AttrVal($hash->{NAME}, "reading", undef);
  2413. my $device = AttrVal($hash->{NAME}, "device", undef);
  2414. # only for this block because of warnings if details of readings are not set
  2415. no warnings 'uninitialized';
  2416. my $ds = $device." -- " if ($device);
  2417. my $rds = $reading." -- " if ($reading);
  2418. my $export_string = $ds.$rds." -- ROWS EXPORTED TO FILE -- ";
  2419. readingsBeginUpdate($hash);
  2420. readingsBulkUpdate($hash, $export_string, $nrows);
  2421. readingsBulkUpdate($hash, "background_processing_time", sprintf("%.4f",$brt)) if(AttrVal($name, "showproctime", undef));
  2422. readingsBulkUpdate($hash, "sql_processing_time", sprintf("%.4f",$rt)) if(AttrVal($name, "showproctime", undef));
  2423. readingsBulkUpdate($hash, "state", "done");
  2424. readingsEndUpdate($hash, 1);
  2425. my $rows = $ds.$rds.$nrows;
  2426. Log3 ($name, 3, "DbRep $name - Number of exported datasets from $hash->{DATABASE} to file ".AttrVal($name, "expimpfile", undef).": $rows");
  2427. delete($hash->{HELPER}{RUNNING_PID});
  2428. Log3 ($name, 4, "DbRep $name -> BlockingCall expfile_ParseDone finished");
  2429. return;
  2430. }
  2431. ####################################################################################################
  2432. # nichtblockierende DB-Funktion impfile
  2433. ####################################################################################################
  2434. sub impfile_Push($) {
  2435. my ($name) = @_;
  2436. my $hash = $defs{$name};
  2437. my $dbloghash = $hash->{dbloghash};
  2438. my $dbconn = $dbloghash->{dbconn};
  2439. my $dbuser = $dbloghash->{dbuser};
  2440. my $dblogname = $dbloghash->{NAME};
  2441. my $dbmodel = $hash->{dbloghash}{DBMODEL};
  2442. my $dbpassword = $attr{"sec$dblogname"}{secret};
  2443. my $err=0;
  2444. # Background-Startzeit
  2445. my $bst = [gettimeofday];
  2446. Log3 ($name, 4, "DbRep $name -> Start BlockingCall impfile_Push");
  2447. my $dbh;
  2448. eval {$dbh = DBI->connect("dbi:$dbconn", $dbuser, $dbpassword, { PrintError => 0, RaiseError => 1, AutoInactiveDestroy => 1 });};
  2449. if ($@) {
  2450. $err = encode_base64($@,"");
  2451. Log3 ($name, 2, "DbRep $name - $@");
  2452. Log3 ($name, 4, "DbRep $name -> BlockingCall impfile_Push finished");
  2453. return "$name|''|''|$err";
  2454. }
  2455. my $infile = AttrVal($name, "expimpfile", undef);
  2456. if (open(FH, "$infile")) {
  2457. binmode (FH);
  2458. } else {
  2459. $err = encode_base64("could not open ".$infile.": ".$!,"");
  2460. Log3 ($name, 4, "DbRep $name -> BlockingCall impfile_Push finished");
  2461. return "$name|''|''|$err";
  2462. }
  2463. # only for this block because of warnings if details inline is not set
  2464. no warnings 'uninitialized';
  2465. # SQL-Startzeit
  2466. my $st = [gettimeofday];
  2467. my $al;
  2468. # Datei zeilenweise einlesen und verarbeiten !
  2469. # Beispiel Inline:
  2470. # "2016-09-25 08:53:56","STP_5000","SMAUTILS","etotal: 11859.573","etotal","11859.573",""
  2471. $dbh->begin_work();
  2472. my $sth = $dbh->prepare_cached("INSERT INTO history (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES (?,?,?,?,?,?,?)");
  2473. my $irowdone = 0;
  2474. my $irowcount = 0;
  2475. my $warn = 0;
  2476. while (<FH>) {
  2477. $al = $_;
  2478. chomp $al;
  2479. my @alarr = split("\",\"", $al);
  2480. foreach(@alarr) {
  2481. tr/"//d;
  2482. }
  2483. my $i_timestamp = $alarr[0];
  2484. # $i_timestamp =~ tr/"//d;
  2485. my $i_device = $alarr[1];
  2486. my $i_type = $alarr[2];
  2487. my $i_event = $alarr[3];
  2488. my $i_reading = $alarr[4];
  2489. my $i_value = $alarr[5];
  2490. my $i_unit = $alarr[6] ? $alarr[6]: " ";
  2491. $irowcount++;
  2492. next if(!$i_timestamp); #leerer Datensatz
  2493. # check ob TIMESTAMP Format ok ?
  2494. my ($i_date, $i_time) = split(" ",$i_timestamp);
  2495. if ($i_date !~ /(\d{4})-(\d{2})-(\d{2})/ || $i_time !~ /(\d{2}):(\d{2}):(\d{2})/) {
  2496. $err = encode_base64("Format of date/time is not valid in row $irowcount of $infile. Must be format \"YYYY-MM-DD HH:MM:SS\" !","");
  2497. Log3 ($name, 2, "DbRep $name -> ERROR - Import from file $infile was not done. Invalid date/time field format in row $irowcount.");
  2498. close(FH);
  2499. $dbh->rollback;
  2500. Log3 ($name, 4, "DbRep $name -> BlockingCall impfile_Push finished");
  2501. return "$name|''|''|$err";
  2502. }
  2503. # Daten auf maximale Länge (entsprechend der Feldlänge in DbLog DB create-scripts) beschneiden wenn nicht SQLite
  2504. if ($dbmodel ne 'SQLITE') {
  2505. $i_device = substr($i_device,0, $dbrep_col{DEVICE});
  2506. $i_reading = substr($i_reading,0, $dbrep_col{READING});
  2507. $i_value = substr($i_value,0, $dbrep_col{VALUE});
  2508. $i_unit = substr($i_unit,0, $dbrep_col{UNIT}) if($i_unit);
  2509. }
  2510. Log3 ($name, 5, "DbRep $name -> data to insert Timestamp: $i_timestamp, Device: $i_device, Type: $i_type, Event: $i_event, Reading: $i_reading, Value: $i_value, Unit: $i_unit");
  2511. if($i_timestamp && $i_device && $i_reading) {
  2512. eval {$sth->execute($i_timestamp, $i_device, $i_type, $i_event, $i_reading, $i_value, $i_unit);};
  2513. if ($@) {
  2514. $err = encode_base64($@,"");
  2515. Log3 ($name, 2, "DbRep $name - Failed to insert new dataset into database: $@");
  2516. close(FH);
  2517. $dbh->rollback;
  2518. $dbh->disconnect;
  2519. Log3 ($name, 4, "DbRep $name -> BlockingCall impfile_Push finished");
  2520. return "$name|''|''|$err";
  2521. } else {
  2522. $irowdone++
  2523. }
  2524. } else {
  2525. $err = encode_base64("format error in in row $irowcount of $infile.","");
  2526. Log3 ($name, 2, "DbRep $name -> ERROR - Import of datasets of file $infile was NOT done. Formaterror in row $irowcount !");
  2527. close(FH);
  2528. $dbh->rollback;
  2529. $dbh->disconnect;
  2530. Log3 ($name, 4, "DbRep $name -> BlockingCall impfile_Push finished");
  2531. return "$name|''|''|$err";
  2532. }
  2533. }
  2534. $dbh->commit;
  2535. $dbh->disconnect;
  2536. close(FH);
  2537. # SQL-Laufzeit ermitteln
  2538. my $rt = tv_interval($st);
  2539. Log3 ($name, 4, "DbRep $name -> BlockingCall impfile_Push finished");
  2540. # Background-Laufzeit ermitteln
  2541. my $brt = tv_interval($bst);
  2542. $rt = $rt.",".$brt;
  2543. return "$name|$irowdone|$rt|$err";
  2544. }
  2545. ####################################################################################################
  2546. # Auswertungsroutine der nichtblockierenden DB-Funktion impfile
  2547. ####################################################################################################
  2548. sub impfile_PushDone($) {
  2549. my ($string) = @_;
  2550. my @a = split("\\|",$string);
  2551. my $hash = $defs{$a[0]};
  2552. my $irowdone = $a[1];
  2553. my $bt = $a[2];
  2554. my ($rt,$brt) = split(",", $bt);
  2555. my $err = $a[3]?decode_base64($a[3]):undef;
  2556. my $name = $hash->{NAME};
  2557. Log3 ($name, 4, "DbRep $name -> Start BlockingCall impfile_PushDone");
  2558. if ($err) {
  2559. readingsSingleUpdate($hash, "errortext", $err, 1);
  2560. readingsSingleUpdate($hash, "state", "error", 1);
  2561. delete($hash->{HELPER}{RUNNING_PID});
  2562. Log3 ($name, 4, "DbRep $name -> BlockingCall impfile_PushDone finished");
  2563. return;
  2564. }
  2565. # only for this block because of warnings if details of readings are not set
  2566. no warnings 'uninitialized';
  2567. my $import_string = " -- ROWS IMPORTED FROM FILE -- ";
  2568. readingsBeginUpdate($hash);
  2569. readingsBulkUpdate($hash, $import_string, $irowdone);
  2570. readingsBulkUpdate($hash, "background_processing_time", sprintf("%.4f",$brt)) if(AttrVal($name, "showproctime", undef));
  2571. readingsBulkUpdate($hash, "sql_processing_time", sprintf("%.4f",$rt)) if(AttrVal($name, "showproctime", undef));
  2572. readingsBulkUpdate($hash, "state", "done");
  2573. readingsEndUpdate($hash, 1);
  2574. Log3 ($name, 3, "DbRep $name - Number of imported datasets to $hash->{DATABASE} from file ".AttrVal($name, "expimpfile", undef).": $irowdone");
  2575. delete($hash->{HELPER}{RUNNING_PID});
  2576. Log3 ($name, 4, "DbRep $name -> BlockingCall impfile_PushDone finished");
  2577. return;
  2578. }
  2579. ####################################################################################################
  2580. # nichtblockierende DB-Abfrage get db Metadaten
  2581. ####################################################################################################
  2582. sub dbmeta_DoParse($) {
  2583. my ($string) = @_;
  2584. my @a = split("\\|",$string);
  2585. my $name = $a[0];
  2586. my $hash = $defs{$name};
  2587. my $opt = $a[1];
  2588. my $dbloghash = $hash->{dbloghash};
  2589. my $dbconn = $dbloghash->{dbconn};
  2590. my $dbuser = $dbloghash->{dbuser};
  2591. my $dblogname = $dbloghash->{NAME};
  2592. my $dbpassword = $attr{"sec$dblogname"}{secret};
  2593. my $dbmodel = $dbloghash->{DBMODEL};
  2594. my $err;
  2595. # Background-Startzeit
  2596. my $bst = [gettimeofday];
  2597. Log3 ($name, 4, "DbRep $name -> Start BlockingCall dbmeta_DoParse");
  2598. my $dbh;
  2599. eval {$dbh = DBI->connect("dbi:$dbconn", $dbuser, $dbpassword, { PrintError => 0, RaiseError => 1, AutoInactiveDestroy => 1 });};
  2600. if ($@) {
  2601. $err = encode_base64($@,"");
  2602. Log3 ($name, 2, "DbRep $name - $@");
  2603. Log3 ($name, 4, "DbRep $name -> BlockingCall dbmeta_DoParse finished");
  2604. return "$name|''|''|''|$err";
  2605. }
  2606. # only for this block because of warnings if details of readings are not set
  2607. no warnings 'uninitialized';
  2608. # Liste der anzuzeigenden Parameter erzeugen, sonst alle ("%"), abhängig von $opt
  2609. my $param = AttrVal($name, "showVariables", "%") if($opt eq "dbvars");
  2610. $param = AttrVal($name, "showSvrInfo", "[A-Z_]") if($opt eq "svrinfo");
  2611. $param = AttrVal($name, "showStatus", "%") if($opt eq "dbstatus");
  2612. $param = "1" if($opt eq "tableinfo"); # Dummy-Eintrag für einen Schleifendurchlauf
  2613. my @parlist = split(",",$param);
  2614. # SQL-Startzeit
  2615. my $st = [gettimeofday];
  2616. my @row_array;
  2617. my $sth;
  2618. my $sql;
  2619. # due to incompatible changes made in MyQL 5.7.5, see http://johnemb.blogspot.de/2014/09/adding-or-removing-individual-sql-modes.html
  2620. if($dbmodel eq "MYSQL") {
  2621. eval {$dbh->do("SET sql_mode=(SELECT REPLACE(\@\@sql_mode,'ONLY_FULL_GROUP_BY',''));");};
  2622. }
  2623. if ($@) {
  2624. $err = encode_base64($@,"");
  2625. Log3 ($name, 2, "DbRep $name - $@");
  2626. $dbh->disconnect;
  2627. Log3 ($name, 4, "DbRep $name -> BlockingCall dbmeta_DoParse finished");
  2628. return "$name|''|''|''|$err";
  2629. }
  2630. if ($opt ne "svrinfo") {
  2631. foreach my $ple (@parlist) {
  2632. if ($opt eq "dbvars") {
  2633. $sql = "show global variables like '$ple';";
  2634. } elsif ($opt eq "dbstatus") {
  2635. $sql = "show global status like '$ple';";
  2636. } elsif ($opt eq "tableinfo") {
  2637. $sql = "select
  2638. table_name,
  2639. table_schema,
  2640. round(sum(data_length+index_length)/1024/1024,2),
  2641. round(data_free/1024/1024,2),
  2642. row_format,
  2643. table_collation,
  2644. engine,
  2645. table_type,
  2646. create_time
  2647. from information_schema.tables group by 1;";
  2648. }
  2649. Log3($name, 4, "DbRep $name - SQL execute: $sql");
  2650. $sth = $dbh->prepare($sql);
  2651. eval {$sth->execute();};
  2652. if ($@) {
  2653. $err = encode_base64($@,"");
  2654. Log3 ($name, 2, "DbRep $name - $@");
  2655. $dbh->disconnect;
  2656. Log3 ($name, 4, "DbRep $name -> BlockingCall dbmeta_DoParse finished");
  2657. return "$name|''|''|''|$err";
  2658. } else {
  2659. while (my @line = $sth->fetchrow_array()) {
  2660. Log3 ($name, 5, "DbRep $name - SQL result: @line");
  2661. my $row = join("§", @line);
  2662. $row =~ s/ /_/g;
  2663. @line = split("§", $row);
  2664. if ($opt eq "tableinfo") {
  2665. $param = AttrVal($name, "showTableInfo", "[A-Z_]");
  2666. $param =~ s/,/\|/g;
  2667. $param =~ tr/%//d;
  2668. if($line[0] =~ m/($param)/i) {
  2669. push(@row_array, $line[0].".table_schema ".$line[1]);
  2670. push(@row_array, $line[0].".data_index_length_MB ".$line[2]);
  2671. push(@row_array, $line[0].".table_name ".$line[1]);
  2672. push(@row_array, $line[0].".data_free_MB ".$line[3]);
  2673. push(@row_array, $line[0].".row_format ".$line[4]);
  2674. push(@row_array, $line[0].".table_collation ".$line[5]);
  2675. push(@row_array, $line[0].".engine ".$line[6]);
  2676. push(@row_array, $line[0].".table_type ".$line[7]);
  2677. push(@row_array, $line[0].".create_time ".$line[8]);
  2678. }
  2679. } else {
  2680. push(@row_array, $line[0]." ".$line[1]);
  2681. }
  2682. }
  2683. }
  2684. $sth->finish;
  2685. }
  2686. } else {
  2687. $param =~ s/,/\|/g;
  2688. $param =~ tr/%//d;
  2689. # Log3 ($name, 5, "DbRep $name - showDbInfo: $param");
  2690. if($dbmodel eq 'SQLITE') {
  2691. my $sf = $dbh->sqlite_db_filename();
  2692. if ($@) {
  2693. $err = encode_base64($@,"");
  2694. Log3 ($name, 2, "DbRep $name - $@");
  2695. $dbh->disconnect;
  2696. Log3 ($name, 4, "DbRep $name -> BlockingCall dbmeta_DoParse finished");
  2697. return "$name|''|''|''|$err";
  2698. } else {
  2699. my $key = "SQLITE_DB_FILENAME";
  2700. push(@row_array, $key." ".$sf) if($key =~ m/($param)/i);
  2701. }
  2702. my @a = split(' ',qx(du -m /opt/fhem/fhem.db)) if ($^O =~ m/linux/i || $^O =~ m/unix/i);
  2703. my $key = "SQLITE_FILE_SIZE_MB";
  2704. push(@row_array, $key." ".$a[0]) if($key =~ m/($param)/i);
  2705. }
  2706. my $info;
  2707. while( my ($key,$value) = each(%GetInfoType) ) {
  2708. eval { $info = $dbh->get_info($GetInfoType{"$key"}) };
  2709. if ($@) {
  2710. $err = encode_base64($@,"");
  2711. Log3 ($name, 2, "DbRep $name - $@");
  2712. $dbh->disconnect;
  2713. Log3 ($name, 4, "DbRep $name -> BlockingCall dbmeta_DoParse finished");
  2714. return "$name|''|''|''|$err";
  2715. } else {
  2716. push(@row_array, $key." ".$info) if($key =~ m/($param)/i);
  2717. }
  2718. }
  2719. }
  2720. # SQL-Laufzeit ermitteln
  2721. my $rt = tv_interval($st);
  2722. $dbh->disconnect;
  2723. my $rowlist = join('§', @row_array);
  2724. Log3 ($name, 5, "DbRep $name -> row_array: \n@row_array");
  2725. # Daten müssen als Einzeiler zurückgegeben werden
  2726. $rowlist = encode_base64($rowlist,"");
  2727. Log3 ($name, 4, "DbRep $name -> BlockingCall dbmeta_DoParse finished");
  2728. # Background-Laufzeit ermitteln
  2729. my $brt = tv_interval($bst);
  2730. $rt = $rt.",".$brt;
  2731. return "$name|$rowlist|$rt|$opt|0";
  2732. }
  2733. ####################################################################################################
  2734. # Auswertungsroutine der nichtblockierenden DB-Abfrage get db Metadaten
  2735. ####################################################################################################
  2736. sub dbmeta_ParseDone($) {
  2737. my ($string) = @_;
  2738. my @a = split("\\|",$string);
  2739. my $hash = $defs{$a[0]};
  2740. my $name = $hash->{NAME};
  2741. my $rowlist = decode_base64($a[1]);
  2742. my $bt = $a[2];
  2743. my $opt = $a[3];
  2744. my ($rt,$brt) = split(",", $bt);
  2745. my $err = $a[4]?decode_base64($a[4]):undef;
  2746. Log3 ($name, 4, "DbRep $name -> Start BlockingCall dbmeta_ParseDone");
  2747. if ($err) {
  2748. readingsSingleUpdate($hash, "errortext", $err, 1);
  2749. readingsSingleUpdate($hash, "state", "error", 1);
  2750. delete($hash->{HELPER}{RUNNING_PID});
  2751. Log3 ($name, 4, "DbRep $name -> BlockingCall dbmeta_ParseDone finished");
  2752. return;
  2753. }
  2754. # only for this block because of warnings if details of readings are not set
  2755. no warnings 'uninitialized';
  2756. # Readingaufbereitung
  2757. readingsBeginUpdate($hash);
  2758. my @row_array = split("§", $rowlist);
  2759. Log3 ($name, 5, "DbRep $name - SQL result decoded: \n@row_array") if(@row_array);
  2760. my $pre = "VAR_" if($opt eq "dbvars");
  2761. $pre = "STAT_" if($opt eq "dbstatus");
  2762. $pre = "INFO_" if($opt eq "tableinfo");
  2763. $pre = "" if($opt eq "svrinfo");
  2764. foreach my $row (@row_array) {
  2765. my @a = split(" ", $row);
  2766. my $k = $a[0];
  2767. my $v = $a[1];
  2768. readingsBulkUpdate($hash, $pre.$k, $v);
  2769. }
  2770. readingsBulkUpdate($hash, "background_processing_time", sprintf("%.4f",$brt)) if(AttrVal($name, "showproctime", undef));
  2771. readingsBulkUpdate($hash, "sql_processing_time", sprintf("%.4f",$rt)) if(AttrVal($name, "showproctime", undef));
  2772. readingsBulkUpdate($hash, "state", "done");
  2773. readingsEndUpdate($hash, 1);
  2774. # InternalTimer(time+0.5, "browser_refresh", $hash, 0);
  2775. delete($hash->{HELPER}{RUNNING_PID});
  2776. Log3 ($name, 4, "DbRep $name -> BlockingCall dbmeta_ParseDone finished");
  2777. return;
  2778. }
  2779. ####################################################################################################
  2780. # Abbruchroutine Timeout DB-Abfrage
  2781. ####################################################################################################
  2782. sub ParseAborted($) {
  2783. my ($hash) = @_;
  2784. my $name = $hash->{NAME};
  2785. my $dbh = $hash->{DBH};
  2786. Log3 ($name, 1, "DbRep $name -> BlockingCall $hash->{HELPER}{RUNNING_PID}{fn} timed out");
  2787. $dbh->disconnect() if(defined($dbh));
  2788. readingsSingleUpdate($hash, "state", "timeout", 1);
  2789. delete($hash->{HELPER}{RUNNING_PID});
  2790. }
  2791. ####################################################################################################
  2792. # Browser Refresh nach DB-Abfrage
  2793. ####################################################################################################
  2794. sub browser_refresh($) {
  2795. my ($hash) = @_;
  2796. RemoveInternalTimer($hash, "browser_refresh");
  2797. {FW_directNotify("#FHEMWEB:WEB", "location.reload('true')", "")};
  2798. # map { FW_directNotify("#FHEMWEB:$_", "location.reload(true)", "") } devspec2array("WEB.*");
  2799. return;
  2800. }
  2801. ####################################################################################################
  2802. # Zusammenstellung Aggregationszeiträume
  2803. ####################################################################################################
  2804. sub collaggstr($$$$) {
  2805. my ($hash,$runtime,$i,$runtime_string_next) = @_;
  2806. my $name = $hash->{NAME};
  2807. my $runtime_string; # Datum/Zeit im SQL-Format für Readingname Teilstring
  2808. my $runtime_string_first; # Datum/Zeit Auswertungsbeginn im SQL-Format für SQL-Statement
  2809. my $ll; # loopindikator, wenn 1 = loopausstieg
  2810. my $runtime_orig; # orig. runtime als Grundlage für Addition mit $aggsec
  2811. my $tsstr = $hash->{HELPER}{CV}{tsstr}; # für Berechnung Tagesverschieber / Stundenverschieber
  2812. my $testr = $hash->{HELPER}{CV}{testr}; # für Berechnung Tagesverschieber / Stundenverschieber
  2813. my $dsstr = $hash->{HELPER}{CV}{dsstr}; # für Berechnung Tagesverschieber / Stundenverschieber
  2814. my $destr = $hash->{HELPER}{CV}{destr}; # für Berechnung Tagesverschieber / Stundenverschieber
  2815. my $msstr = $hash->{HELPER}{CV}{msstr}; # Startmonat für Berechnung Monatsverschieber
  2816. my $mestr = $hash->{HELPER}{CV}{mestr}; # Endemonat für Berechnung Monatsverschieber
  2817. my $ysstr = $hash->{HELPER}{CV}{ysstr}; # Startjahr für Berechnung Monatsverschieber
  2818. my $yestr = $hash->{HELPER}{CV}{yestr}; # Endejahr für Berechnung Monatsverschieber
  2819. my $aggregation = $hash->{HELPER}{CV}{aggregation}; # Aggregation
  2820. my $aggsec = $hash->{HELPER}{CV}{aggsec}; # laufende Aggregationssekunden
  2821. my $epoch_seconds_end = $hash->{HELPER}{CV}{epoch_seconds_end};
  2822. my $wdadd = $hash->{HELPER}{CV}{wdadd}; # Ergänzungstage. Starttag + Ergänzungstage = der folgende Montag (für week-Aggregation)
  2823. # only for this block because of warnings if some values not set
  2824. no warnings 'uninitialized';
  2825. # keine Aggregation (all between timestamps)
  2826. if ($aggregation eq "no") {
  2827. $runtime_string = "all_between_timestamps"; # für Readingname
  2828. $runtime_string_first = strftime "%Y-%m-%d %H:%M:%S", localtime($runtime);
  2829. $runtime_string_next = strftime "%Y-%m-%d %H:%M:%S", localtime($epoch_seconds_end);
  2830. $ll = 1;
  2831. }
  2832. # Monatsaggregation
  2833. if ($aggregation eq "month") {
  2834. $runtime_orig = $runtime;
  2835. $runtime = $runtime+3600 if(dsttest($hash,$runtime,$aggsec)); # Korrektur Winterzeitumstellung (Uhr wurde 1 Stunde zurück gestellt)
  2836. # Hilfsrechnungen
  2837. my $rm = strftime "%m", localtime($runtime); # Monat des aktuell laufenden Startdatums d. SQL-Select
  2838. my $ry = strftime "%Y", localtime($runtime); # Jahr des aktuell laufenden Startdatums d. SQL-Select
  2839. my $dim = $rm-2?30+($rm*3%7<4):28+!($ry%4||$ry%400*!($ry%100)); # Anzahl Tage des aktuell laufenden Monats f. SQL-Select
  2840. Log3 ($name, 5, "DbRep $name - act year: $ry, act month: $rm, days in month: $dim, endyear: $yestr, endmonth: $mestr");
  2841. $runtime_string = strftime "%Y-%m", localtime($runtime); # für Readingname
  2842. if ($i==1) {
  2843. # nur im ersten Durchlauf
  2844. $runtime_string_first = strftime "%Y-%m-%d %H:%M:%S", localtime($runtime_orig);
  2845. }
  2846. if ($ysstr == $yestr && $msstr == $mestr || $ry == $yestr && $rm == $mestr) {
  2847. $runtime_string_first = strftime "%Y-%m-01", localtime($runtime) if($i>1);
  2848. $runtime_string_next = strftime "%Y-%m-%d %H:%M:%S", localtime($epoch_seconds_end);
  2849. $ll=1;
  2850. } else {
  2851. if(($runtime) > $epoch_seconds_end) {
  2852. $runtime_string_first = strftime "%Y-%m-01", localtime($runtime) if($i>11);
  2853. $runtime_string_next = strftime "%Y-%m-%d %H:%M:%S", localtime($epoch_seconds_end);
  2854. $ll=1;
  2855. } else {
  2856. $runtime_string_first = strftime "%Y-%m-01", localtime($runtime) if($i>1);
  2857. $runtime_string_next = strftime "%Y-%m-01", localtime($runtime+($dim*86400));
  2858. }
  2859. }
  2860. my ($yyyy1, $mm1, $dd1) = ($runtime_string_next =~ /(\d+)-(\d+)-(\d+)/);
  2861. $runtime = timelocal("00", "00", "00", "01", $mm1-1, $yyyy1-1900);
  2862. # neue Beginnzeit in Epoche-Sekunden
  2863. $runtime = $runtime_orig+$aggsec;
  2864. }
  2865. # Wochenaggregation
  2866. if ($aggregation eq "week") {
  2867. $runtime = $runtime+3600 if($i!=1 && dsttest($hash,$runtime,$aggsec)); # Korrektur Winterzeitumstellung (Uhr wurde 1 Stunde zurück gestellt)
  2868. $runtime_orig = $runtime;
  2869. my $w = strftime "%V", localtime($runtime); # Wochennummer des aktuellen Startdatum/Zeit
  2870. $runtime_string = "week_".$w; # für Readingname
  2871. my $ms = strftime "%m", localtime($runtime); # Startmonat (01-12)
  2872. my $me = strftime "%m", localtime($epoch_seconds_end); # Endemonat (01-12)
  2873. if ($i==1) {
  2874. # nur im ersten Schleifendurchlauf
  2875. $runtime_string_first = strftime "%Y-%m-%d %H:%M:%S", localtime($runtime);
  2876. # Korrektur $runtime_orig für Berechnung neue Beginnzeit für nächsten Durchlauf
  2877. my ($yyyy1, $mm1, $dd1) = ($runtime_string_first =~ /(\d+)-(\d+)-(\d+)/);
  2878. $runtime = timelocal("00", "00", "00", $dd1, $mm1-1, $yyyy1-1900);
  2879. $runtime = $runtime+3600 if(dsttest($hash,$runtime,$aggsec)); # Korrektur Winterzeitumstellung (Uhr wurde 1 Stunde zurück gestellt)
  2880. $runtime = $runtime+$wdadd;
  2881. $runtime_orig = $runtime-$aggsec;
  2882. # die Woche Beginn ist gleich der Woche vom Ende Auswertung
  2883. if((strftime "%V", localtime($epoch_seconds_end)) eq ($w) && ($ms+$me != 13)) {
  2884. $runtime_string_next = strftime "%Y-%m-%d %H:%M:%S", localtime($epoch_seconds_end);
  2885. $ll=1;
  2886. } else {
  2887. $runtime_string_next = strftime "%Y-%m-%d", localtime($runtime);
  2888. }
  2889. } else {
  2890. # weitere Durchläufe
  2891. if(($runtime+$aggsec) > $epoch_seconds_end) {
  2892. $runtime_string_first = strftime "%Y-%m-%d", localtime($runtime_orig);
  2893. $runtime_string_next = strftime "%Y-%m-%d %H:%M:%S", localtime($epoch_seconds_end);
  2894. $ll=1;
  2895. } else {
  2896. $runtime_string_first = strftime "%Y-%m-%d", localtime($runtime_orig) ;
  2897. $runtime_string_next = strftime "%Y-%m-%d", localtime($runtime+$aggsec);
  2898. }
  2899. }
  2900. # neue Beginnzeit in Epoche-Sekunden
  2901. $runtime = $runtime_orig+$aggsec;
  2902. }
  2903. # Tagesaggregation
  2904. if ($aggregation eq "day") {
  2905. $runtime_string = strftime "%Y-%m-%d", localtime($runtime); # für Readingname
  2906. $runtime_string_first = strftime "%Y-%m-%d %H:%M:%S", localtime($runtime) if($i==1);
  2907. $runtime_string_first = strftime "%Y-%m-%d", localtime($runtime) if($i>1);
  2908. $runtime = $runtime+3600 if(dsttest($hash,$runtime,$aggsec)); # Korrektur Winterzeitumstellung (Uhr wurde 1 Stunde zurück gestellt)
  2909. if((($tsstr gt $testr) ? $runtime : ($runtime+$aggsec)) > $epoch_seconds_end) {
  2910. $runtime_string_first = strftime "%Y-%m-%d", localtime($runtime);
  2911. $runtime_string_first = strftime "%Y-%m-%d %H:%M:%S", localtime($runtime) if( $dsstr eq $destr);
  2912. $runtime_string_next = strftime "%Y-%m-%d %H:%M:%S", localtime($epoch_seconds_end);
  2913. $ll=1;
  2914. } else {
  2915. $runtime_string_next = strftime "%Y-%m-%d", localtime($runtime+$aggsec);
  2916. }
  2917. Log3 ($name, 5, "DbRep $name - runtime_string: $runtime_string, runtime_string_first(begin): $runtime_string_first, runtime_string_next(end): $runtime_string_next");
  2918. # neue Beginnzeit in Epoche-Sekunden
  2919. $runtime = $runtime+$aggsec;
  2920. }
  2921. # Stundenaggregation
  2922. if ($aggregation eq "hour") {
  2923. $runtime_string = strftime "%Y-%m-%d_%H", localtime($runtime); # für Readingname
  2924. $runtime_string_first = strftime "%Y-%m-%d %H:%M:%S", localtime($runtime) if($i==1);
  2925. $runtime = $runtime+3600 if(dsttest($hash,$runtime,$aggsec)); # Korrektur Winterzeitumstellung (Uhr wurde 1 Stunde zurück gestellt)
  2926. $runtime_string_first = strftime "%Y-%m-%d %H", localtime($runtime) if($i>1);
  2927. my @a = split (":",$tsstr);
  2928. my $hs = $a[0];
  2929. my $msstr = $a[1].":".$a[2];
  2930. @a = split (":",$testr);
  2931. my $he = $a[0];
  2932. my $mestr = $a[1].":".$a[2];
  2933. if((($msstr gt $mestr) ? $runtime : ($runtime+$aggsec)) > $epoch_seconds_end) {
  2934. $runtime_string_first = strftime "%Y-%m-%d %H", localtime($runtime);
  2935. $runtime_string_first = strftime "%Y-%m-%d %H:%M:%S", localtime($runtime) if( $dsstr eq $destr && $hs eq $he);
  2936. $runtime_string_next = strftime "%Y-%m-%d %H:%M:%S", localtime($epoch_seconds_end);
  2937. $ll=1;
  2938. } else {
  2939. $runtime_string_next = strftime "%Y-%m-%d %H", localtime($runtime+$aggsec);
  2940. }
  2941. # neue Beginnzeit in Epoche-Sekunden
  2942. $runtime = $runtime+$aggsec;
  2943. }
  2944. return ($runtime,$runtime_string,$runtime_string_first,$runtime_string_next,$ll);
  2945. }
  2946. ####################################################################################################
  2947. # Test auf Daylight saving time
  2948. ####################################################################################################
  2949. sub dsttest ($$$) {
  2950. my ($hash,$runtime,$aggsec) = @_;
  2951. my $name = $hash->{NAME};
  2952. my $dstchange = 0;
  2953. # der Wechsel der daylight saving time wird dadurch getestet, dass geprüft wird
  2954. # ob im Vergleich der aktuellen zur nächsten Selektionsperiode von "$aggsec (day, week, month)"
  2955. # ein Wechsel der daylight saving time vorliegt
  2956. my $dst = (localtime($runtime))[8]; # ermitteln daylight saving aktuelle runtime
  2957. my $time_str = localtime($runtime+$aggsec); # textual time representation
  2958. my $dst_new = (localtime($runtime+$aggsec))[8]; # ermitteln daylight saving nächste runtime
  2959. if ($dst != $dst_new) {
  2960. $dstchange = 1;
  2961. }
  2962. Log3 ($name, 5, "DbRep $name - Daylight savings changed: $dstchange (on $time_str)");
  2963. return $dstchange;
  2964. }
  2965. ####################################################################################################
  2966. # Counthash Untersuchung
  2967. # Logausgabe der Anzahl verarbeiteter Datensätze pro Zeitraum / Aggregation
  2968. # Rückgabe eines ncp-hash (no calc in period) mit den Perioden für die keine Differenz berechnet
  2969. # werden konnte weil nur ein Datensatz in der Periode zur Verfügung stand
  2970. ####################################################################################################
  2971. sub calcount ($$) {
  2972. my ($hash,$ch) = @_;
  2973. my $name = $hash->{NAME};
  2974. my %ncp = ();
  2975. Log3 ($name, 4, "DbRep $name - count of values used for calc:");
  2976. foreach my $key (sort(keys%{$ch})) {
  2977. Log3 ($name, 4, "$key => ". $ch->{$key});
  2978. if($ch->{$key} eq "1") {
  2979. $ncp{"$key"} = " ||";
  2980. }
  2981. }
  2982. return \%ncp;
  2983. }
  2984. ####################################################################################################
  2985. # Test-Sub zu Testzwecken
  2986. ####################################################################################################
  2987. sub testexit ($) {
  2988. my ($hash) = @_;
  2989. my $name = $hash->{NAME};
  2990. if ( !DbRep_Connect($hash) ) {
  2991. Log3 ($name, 2, "DbRep $name - DB connect failed. Database down ? ");
  2992. readingsSingleUpdate($hash, "state", "disconnected", 1);
  2993. return;
  2994. } else {
  2995. my $dbh = $hash->{DBH};
  2996. Log3 ($name, 3, "DbRep $name - --------------- FILE INFO --------------");
  2997. my $sqlfile = $dbh->sqlite_db_filename();
  2998. Log3 ($name, 3, "DbRep $name - FILE : $sqlfile ");
  2999. # # $dbh->table_info( $catalog, $schema, $table)
  3000. # my $sth = $dbh->table_info('', '%', '%');
  3001. # my $tables = $dbh->selectcol_arrayref($sth, {Columns => [3]});
  3002. # my $table = join ', ', @$tables;
  3003. # Log3 ($name, 3, "DbRep $name - SQL_TABLES : $table");
  3004. Log3 ($name, 3, "DbRep $name - --------------- PRAGMA --------------");
  3005. my @InfoTypes = ('sqlite_db_status');
  3006. foreach my $row (@InfoTypes) {
  3007. # my @linehash = $dbh->$row;
  3008. my $array= $dbh->$row ;
  3009. # push(@row_array, @array);
  3010. while ((my $key, my $val) = each %{$array}) {
  3011. Log3 ($name, 3, "DbRep $name - PRAGMA : $key : ".%{$val});
  3012. }
  3013. }
  3014. # $sth->finish;
  3015. $dbh->disconnect;
  3016. }
  3017. return;
  3018. }
  3019. 1;
  3020. =pod
  3021. =item helper
  3022. =item summary Reporting & Management content of DbLog-DB's. Content is depicted as readings
  3023. =item summary_DE Reporting & Management von DbLog-DB Content. Darstellung als Readings
  3024. =begin html
  3025. <a name="DbRep"></a>
  3026. <h3>DbRep</h3>
  3027. <ul>
  3028. <br>
  3029. The purpose of this module is browsing and managing the content of DbLog-databases. The searchresults can be evaluated concerning to various aggregations and the appropriate
  3030. Readings will be filled. The data selection will been done by declaration of device, reading and the time settings of selection-begin and selection-end. <br><br>
  3031. All database operations are implemented nonblocking. Optional the execution time of SQL-statements in background can also be determined and provided as reading.
  3032. (refer to <a href="#DbRepattr">attributes</a>). <br>
  3033. All existing readings will be deleted when a new operation starts. By attribute "readingPreventFromDel" a comma separated list of readings which are should prevent
  3034. from deletion can be provided. <br><br>
  3035. Currently the following functions are provided: <br><br>
  3036. <ul><ul>
  3037. <li> Selection of all datasets within adjustable time limits. </li>
  3038. <li> Exposure of datasets of a Device/Reading-combination within adjustable time limits. </li>
  3039. <li> Selecion of datasets by usage of dynamically calclated time limits at execution time. </li>
  3040. <li> Calculation of quantity of datasets of a Device/Reading-combination within adjustable time limits and several aggregations. </li>
  3041. <li> The calculation of summary- , difference- , maximum- , minimum- and averageValues of numeric readings within adjustable time limits and several aggregations. </li>
  3042. <li> The deletion of datasets. The containment of deletion can be done by Device and/or Reading as well as fix or dynamically calculated time limits at execution time. </li>
  3043. <li> export of datasets to file (CSV-format). </li>
  3044. <li> import of datasets from file (CSV-Format). </li>
  3045. <li> rename of device names in datasets </li>
  3046. <li> automatic rename of device names in datasets and other DbRep-definitions after FHEM "rename" command (see <a href="#DbRepAutoRename">DbRep-Agent</a>) </li>
  3047. </ul></ul>
  3048. <br>
  3049. To activate the function "Autorename" the attribute "role" has to be assigned to a defined DbRep-device. The standard role after DbRep definition is "Client.
  3050. Please read more in section <a href="#DbRepAutoRename">DbRep-Agent</a> . <br><br>
  3051. FHEM-Forum: <br>
  3052. <a href="https://forum.fhem.de/index.php/topic,53584.msg452567.html#msg452567">Modul 93_DbRep - Reporting and Management of database content (DbLog)</a>.<br><br>
  3053. <br>
  3054. <b>Preparations </b> <br><br>
  3055. The module requires the usage of a DbLog instance and the credentials of the database definition will be used. (currently tested with MySQL and SQLite). <br>
  3056. Only the content of table "history" will be included. <br><br>
  3057. Overview which other Perl-modules DbRep is using: <br><br>
  3058. POSIX <br>
  3059. Time::HiRes <br>
  3060. Time::Local <br>
  3061. Scalar::Util <br>
  3062. DBI <br>
  3063. Blocking (FHEM-module) <br><br>
  3064. Due to performance reason the following index should be created in addition: <br>
  3065. <code>
  3066. ALTER TABLE 'fhem'.'history' ADD INDEX `Reading_Time_Idx` (`READING`, `TIMESTAMP`) USING BTREE;
  3067. </code>
  3068. </ul>
  3069. <br>
  3070. <a name="DbRepdefine"></a>
  3071. <b>Definition</b>
  3072. <br>
  3073. <ul>
  3074. <code>
  3075. define &lt;name&gt; DbRep &lt;name of DbLog-instance&gt;
  3076. </code>
  3077. <br><br>
  3078. (&lt;name of DbLog-instance&gt; - name of the database instance which is wanted to analyze needs to be inserted)
  3079. </ul>
  3080. <br><br>
  3081. <a name="DbRepset"></a>
  3082. <b>Set </b>
  3083. <ul>
  3084. Currently following set-commands are included. They are used to trigger the evaluations and define the evaluation option option itself.
  3085. The criteria of searching database content and determine aggregation is carried out by setting several <a href="#DbRepattr">attributes</a>.
  3086. <br><br>
  3087. <ul><ul>
  3088. <li><b> averageValue </b> - calculates the average value of readingvalues DB-column "VALUE") between period given by timestamp-<a href="#DbRepattr">attributes</a> which are set.
  3089. The reading to evaluate must be defined using attribute "reading". </li> <br>
  3090. <li><b> countEntries </b> - provides the number of DB-entries between period given by timestamp-<a href="#DbRepattr">attributes</a> which are set.
  3091. If timestamp-attributes are not set, all entries in db will be count. The <a href="#DbRepattr">attributes</a> "device" and "reading" can be used to limit the evaluation. </li> <br>
  3092. <li><b> deviceRename </b> - renames the device name of a device inside the connected database (Internal DATABASE).
  3093. The devicename will allways be changed in the <b>entire</b> database. Possibly set time limits or restrictions by
  3094. <a href="#DbRepattr">attributes</a> device and/or reading will not be considered. <br><br>
  3095. <ul>
  3096. <b>input format: </b> set &lt;name&gt; deviceRename &lt;old device name&gt;,&lt;new device name&gt; <br>
  3097. # The amount of renamed device names (datasets) will be displayed in reading "device_renamed". <br>
  3098. # If the device name to be renamed was not found in the database, a WARNUNG will appear in reading "device_not_renamed". <br>
  3099. # Appropriate entries will be written to Logfile if verbose >= 3 is set.
  3100. <br><br>
  3101. </li> <br>
  3102. </ul>
  3103. <li><b> readingRename </b> - renames the reading name of a device inside the connected database (see Internal DATABASE).
  3104. The readingname will allways be changed in the <b>entire</b> database. Possibly set time limits or restrictions by
  3105. <a href="#DbRepattr">attributes</a> device and/or reading will not be considered. <br><br>
  3106. <ul>
  3107. <b>input format: </b> set &lt;name&gt; readingRename &lt;old reading name&gt;,&lt;new reading name&gt; <br>
  3108. # The amount of renamed reading names (datasets) will be displayed in reading "reading_renamed". <br>
  3109. # If the reading name to be renamed was not found in the database, a WARNUNG will appear in reading "reading_not_renamed". <br>
  3110. # Appropriate entries will be written to Logfile if verbose >= 3 is set.
  3111. <br><br>
  3112. </li> <br>
  3113. </ul>
  3114. <li><b> exportToFile </b> - exports DB-entries to a file in CSV-format between period given by timestamp.
  3115. Limitations of selections can be set by <a href="#DbRepattr">attributes</a> Device and/or Reading.
  3116. The filename will be defined by <a href="#DbRepattr">attribute</a> "expimpfile" . </li> <br>
  3117. <li><b> fetchrows </b> - provides <b>all</b> DB-entries between period given by timestamp-<a href="#DbRepattr">attributes</a>.
  3118. An aggregation which would possibly be set attribute will <b>not</b> considered. </li> <br>
  3119. <li><b> insert </b> - use it to insert data ito table "history" manually. Input values for Date, Time and Value are mandatory. The database fields for Type and Event will be filled in with "manual" automatically and the values of Device, Reading will be get from set <a href="#DbRepattr">attributes</a>. <br><br>
  3120. <ul>
  3121. <b>input format: </b> Date,Time,Value,[Unit] <br>
  3122. # Unit is optional, attributes of device, reading must be set ! <br>
  3123. # If "Value=0" has to be inserted, use "Value = 0.0" to do it. <br><br>
  3124. <b>example:</b> 2016-08-01,23:00:09,TestValue,TestUnit <br>
  3125. # field length is maximum 32 (MYSQL) / 64 (POSTGRESQL) characters long, Spaces are NOT allowed in fieldvalues ! <br>
  3126. <br>
  3127. <b>Note: </b><br>
  3128. Please consider to insert AT LEAST two datasets into the intended time / aggregatiom period (day, week, month, etc.) because of
  3129. it's needed by function diffValue. Otherwise no difference can be calculated and diffValue will be print out "0" for the respective period !
  3130. <br>
  3131. <br>
  3132. </li>
  3133. </ul>
  3134. <li><b> importFromFile </b> - imports datasets in CSV format from file into database. The filename will be set by <a href="#DbRepattr">attribute</a> "expimpfile". <br><br>
  3135. <ul>
  3136. <b>dataset format: </b> "TIMESTAMP","DEVICE","TYPE","EVENT","READING","VALUE","UNIT" <br><br>
  3137. # The fields "TIMESTAMP","DEVICE","TYPE","EVENT","READING" and "VALUE" have to be set. The field "UNIT" is optional.
  3138. The file content will be imported transactional. That means all of the content will be imported or, in case of error, nothing of it.
  3139. If an extensive file will be used, DON'T set verbose = 5 because of a lot of datas would be written to the logfile in this case.
  3140. It could lead to blocking or overload FHEM ! <br><br>
  3141. <b>Example: </b> "2016-09-25 08:53:56","STP_5000","SMAUTILS","etotal: 11859.573","etotal","11859.573","" <br>
  3142. <br>
  3143. </li> <br>
  3144. </ul>
  3145. <li><b> sumValue </b> - calculates the amount of readingvalues DB-column "VALUE") between period given by <a href="#DbRepattr">attributes</a> "timestamp_begin", "timestamp_end" or "timeDiffToNow / timeOlderThan". The reading to evaluate must be defined using attribute "reading". Using this function is mostly reasonable if value-differences of readings are written to the database. </li> <br>
  3146. <li><b> maxValue </b> - calculates the maximum value of readingvalues DB-column "VALUE") between period given by <a href="#DbRepattr">attributes</a> "timestamp_begin", "timestamp_end" or "timeDiffToNow / timeOlderThan".
  3147. The reading to evaluate must be defined using attribute "reading".
  3148. The evaluation contains the timestamp of the <b>last</b> appearing of the identified maximum value within the given period. </li> <br>
  3149. <li><b> minValue </b> - calculates the miniimum value of readingvalues DB-column "VALUE") between period given by <a href="#DbRepattr">attributes</a> "timestamp_begin", "timestamp_end" or "timeDiffToNow / timeOlderThan".
  3150. The reading to evaluate must be defined using attribute "reading".
  3151. The evaluation contains the timestamp of the <b>first</b> appearing of the identified minimum value within the given period. </li> <br>
  3152. <li><b> diffValue </b> - calculates the defference of the readingvalues DB-column "VALUE") between period given by <a href="#DbRepattr">attributes</a> "timestamp_begin", "timestamp_end" or "timeDiffToNow / timeOlderThan".
  3153. The reading to evaluate must be defined using attribute "reading".
  3154. This function is mostly reasonable if readingvalues are increasing permanently and don't write value-differences to the database.
  3155. The difference will be generated from the first available dataset (VALUE-Field) to the last available dataset between the
  3156. specified time linits/aggregation, in which a balanced difference value of the previous aggregation period will be transfered to the
  3157. following aggregation period in case this period contains a value. <br>
  3158. An possible counter overrun (restart with value "0") will be considered (compare <a href="#DbRepattr">attribute</a> "diffAccept"). <br>
  3159. If only one dataset will be found within the evalution period, the difference can be calculated only in combination with the balanced
  3160. difference of the previous aggregation period. In this case a logical inaccuracy according the assignment of the difference to the particular aggregation period
  3161. can be possible. Hence in warning in "state" will be placed and the reading "less_data_in_period" with a list of periods
  3162. with only one dataset found in it will be created. <br><br>
  3163. no difference can be calculated
  3164. and the reading "less_data_in_period" with a list of concerned periods will be generated in that case. <br><br>
  3165. <ul>
  3166. <b>Note: </b><br>
  3167. Within the evaluation respectively aggregation period (day, week, month, etc.) you should make available at least one dataset
  3168. at the beginning and one dataset at the end of each aggregation period to take the difference calculation as much as possible.
  3169. <br>
  3170. <br>
  3171. </li>
  3172. </ul>
  3173. <li><b> delEntries </b> - deletes all database entries or only the database entries specified by <a href="#DbRepattr">attributes</a> Device and/or
  3174. Reading and the entered time period between "timestamp_begin", "timestamp_end" (if set) or "timeDiffToNow/timeOlderThan". <br><br>
  3175. <ul>
  3176. "timestamp_begin" is set: deletes db entries <b>from</b> this timestamp until current date/time <br>
  3177. "timestamp_end" is set : deletes db entries <b>until</b> this timestamp <br>
  3178. both Timestamps are set : deletes db entries <b>between</b> these timestamps <br><br>
  3179. Due to security reasons the attribute "allowDeletion" needs to be set to unlock the delete-function. <br>
  3180. </li>
  3181. </ul>
  3182. <br>
  3183. </ul></ul>
  3184. <b>For all evaluation variants applies: </b> <br>
  3185. In addition to the needed reading the device can be complemented to restrict the datasets for reporting / function.
  3186. If the time limit attributes are not set, the period from '1970-01-01 01:00:00' to the current date/time will be used as selection criterion.
  3187. <br><br>
  3188. <b>Note: </b> <br>
  3189. If you are in detail view it could be necessary to refresh the browser to see the result of operation as soon in DeviceOverview section "state = done" will be shown.
  3190. <br><br>
  3191. </ul>
  3192. <a name="DbRepget"></a>
  3193. <b>Get </b>
  3194. <ul>
  3195. The get-commands of DbRep provide to retrieve some metadata of the used database instance.
  3196. Those are for example adjusted server parameter, server variables, datadasestatus- and table informations. THe available get-functions depending of
  3197. the used database type. So for SQLite curently only "get svrinfo" is usable. The functions nativ are delivering a lot of outpit values.
  3198. They can be limited by function specific <a href="#DbRepattr">attributes</a>. The filter has to be setup by a comma separated list.
  3199. SQL-Wildcards (% _) can be used to setup the list arguments.
  3200. <br><br>
  3201. <b>Note: </b> <br>
  3202. After executing a get-funktion in detail view please make a browser refresh to see the results !
  3203. <br><br>
  3204. <ul><ul>
  3205. <li><b> dbstatus </b> - lists global informations about MySQL server status (e.g. informations related to cache, threads, bufferpools, etc. ).
  3206. Initially all available informations are reported. Using the <a href="#DbRepattr">attribute</a> "showStatus" the quantity of
  3207. results can be limited to show only the desired values. Further detailed informations of items meaning are
  3208. explained <a href=http://dev.mysql.com/doc/refman/5.7/en/server-status-variables.html>there</a>. <br>
  3209. <br><ul>
  3210. Example: <br>
  3211. get &lt;name&gt; dbstatus <br>
  3212. attr &lt;name&gt; showStatus %uptime%,%qcache% <br>
  3213. # Only readings containing "uptime" and "qcache" in name will be created
  3214. </li>
  3215. <br><br>
  3216. </ul>
  3217. <li><b> dbvars </b> - lists global informations about MySQL system variables. Included are e.g. readings related to InnoDB-Home, datafile path,
  3218. memory- or cache-parameter and so on. The Output reports initially all available informations. Using the
  3219. <a href="#DbRepattr">attribute</a> "showVariables" the quantity of results can be limited to show only the desired values.
  3220. Further detailed informations of items meaning are explained
  3221. <a href=http://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html>there</a>. <br>
  3222. <br><ul>
  3223. Example: <br>
  3224. get &lt;name&gt; dbvars <br>
  3225. attr &lt;name&gt; showVariables %version%,%query_cache% <br>
  3226. # Only readings containing "version" and "query_cache" in name will be created
  3227. </li>
  3228. <br><br>
  3229. </ul>
  3230. <li><b> svrinfo </b> - common database server informations, e.g. DBMS-version, server address and port and so on. The quantity of elements to get depends
  3231. on the database type. Using the <a href="#DbRepattr">attribute</a> "showSvrInfo" the quantity of results can be limited to show only
  3232. the desired values. Further detailed informations of items meaning are explained
  3233. <a href=https://msdn.microsoft.com/en-us/library/ms711681(v=vs.85).aspx>there</a>. <br>
  3234. <br><ul>
  3235. Example: <br>
  3236. get &lt;name&gt; svrinfo <br>
  3237. attr &lt;name&gt; showSvrInfo %SQL_CATALOG_TERM%,%NAME% <br>
  3238. # Only readings containing "SQL_CATALOG_TERM" and "NAME" in name will be created
  3239. </li>
  3240. <br><br>
  3241. </ul>
  3242. <li><b> tableinfo </b> - access detailed informations about tables in MySQL database schema. The analyzed schematics are depend on the rights of the
  3243. used database user (default: the database schema of tables current,history).
  3244. Using the<a href="#DbRepattr">attribute</a> "showTableInfo" the results can be limited. Further detailed informations
  3245. of items meaning are explained <a href=http://dev.mysql.com/doc/refman/5.7/en/show-table-status.html>there</a>. <br>
  3246. <br><ul>
  3247. Example: <br>
  3248. get &lt;name&gt; tableinfo <br>
  3249. attr &lt;name&gt; showTableInfo current,history <br>
  3250. # Only informations related to tables "current" and "history" will be created
  3251. </li>
  3252. <br><br>
  3253. </ul>
  3254. <br>
  3255. </ul></ul>
  3256. </ul>
  3257. <a name="DbRepattr"></a>
  3258. <b>Attributes</b>
  3259. <br>
  3260. <ul>
  3261. Using the module specific attributes you are able to define the scope of evaluation and the aggregation. <br><br>
  3262. <b>Hint to SQL-Wildcard Usage:</b> <br>
  3263. Within the attribute values of "device" and "reading" you may use SQL-Wildcards, "%" and "_". The character "%" stands for any some characters, but
  3264. the character "_" = stands for only one. <br>
  3265. This rule is valid to all functions <b>except</b> "insert", "deviceRename" and "delEntries". <br>
  3266. The function "insert" doesn't allow setting the mentioned attributes containing the wildcard "%", the character "_" will evaluated as a normal character.<br>
  3267. The deletion function "delEntries" evaluates both characters "$", "_" <b>NOT</b> as wildcards and delete device/readings only if they are entered in the
  3268. attribute as exactly as they are stored in the database .
  3269. In readings the wildcard character "%" will be replaced by "/" to meet the rules of allowed characters in readings.
  3270. <br><br>
  3271. <ul><ul>
  3272. <li><b>aggregation </b> - Aggregation of Device/Reading-selections. Possible is hour, day, week, month or "no". Delivers e.g. the count of database entries for a day (countEntries), Summation of difference values of a reading (sumValue) and so on. Using aggregation "no" (default) an aggregation don't happens but the output contaims all values of Device/Reading in the defined time period. </li> <br>
  3273. <li><b>allowDeletion </b> - unlocks the delete-function </li> <br>
  3274. <li><b>device </b> - selection of a particular device </li> <br>
  3275. <li><b>diffAccept </b> - valid for function diffValue. diffAccept determines the threshold, up to that a calaculated difference between two
  3276. straight sequently datasets should be commenly accepted (default = 20). <br>
  3277. Hence faulty DB entries with a disproportional high difference value will be eliminated and don't tamper the result.
  3278. If a threshold overrun happens, the reading "diff_overrun_limit_&lt;diffLimit&gt;" will be generated
  3279. (&lt;diffLimit&gt; will be substituted with the present prest attribute value). <br>
  3280. The reading contains a list of relevant pair of values. Using verbose=3 this list will also be reported in the FHEM
  3281. logfile.
  3282. </li><br>
  3283. <ul>
  3284. Example report in logfile if threshold of diffAccept=10 overruns: <br><br>
  3285. DbRep Rep.STP5000.etotal -> data ignored while calc diffValue due to threshold overrun (diffAccept = 10): <br>
  3286. 2016-04-09 08:50:50 0.0340 -> 2016-04-09 12:42:01 13.3440 <br><br>
  3287. # The first dataset with a value of 0.0340 is untypical low compared to the next value of 13.3440 and results a untypical
  3288. high difference value. <br>
  3289. # Now you have to decide if the (second) dataset should be deleted, ignored of the attribute diffAccept should be adjusted.
  3290. </ul><br>
  3291. <li><b>disable </b> - deactivates the module </li> <br>
  3292. <li><b>expimpfile </b> - Path/filename for data export/import </li> <br>
  3293. <li><b>reading </b> - selection of a particular reading </li> <br>
  3294. <li><b>readingNameMap </b> - the name of the analyzed reading can be overwritten for output </li> <br>
  3295. <li><b>role </b> - the role of the DbRep-device. Standard role is "Client". The role "Agent" is described in section <a href="#DbRepAutoRename">DbRep-Agent</a>. </li> <br>
  3296. <li><b>readingPreventFromDel </b> - comma separated list of readings which are should prevent from deletion when a new operation starts </li> <br>
  3297. <li><b>showproctime </b> - if set, the reading "sql_processing_time" shows the required execution time (in seconds) for the sql-requests. This is not calculated for a single sql-statement, but the summary of all sql-statements necessara for within an executed DbRep-function in background. </li> <br>
  3298. <li><b>showStatus </b> - limits the sample space of command "get ... dbstatus". SQL-Wildcards (% _) can be used. </li> <br>
  3299. <ul>
  3300. Example: attr ... showStatus %uptime%,%qcache% <br>
  3301. # Only readings with containing "uptime" and "qcache" in name will be shown <br>
  3302. </ul><br>
  3303. <li><b>showVariables </b> - limits the sample space of command "get ... dbvars". SQL-Wildcards (% _) can be used. </li> <br>
  3304. <ul>
  3305. Example: attr ... showVariables %version%,%query_cache% <br>
  3306. # Only readings with containing "version" and "query_cache" in name will be shown <br>
  3307. </ul><br>
  3308. <li><b>showSvrInfo </b> - limits the sample space of command "get ... svrinfo". SQL-Wildcards (% _) can be used. </li> <br>
  3309. <ul>
  3310. Example: attr ... showSvrInfo %SQL_CATALOG_TERM%,%NAME% <br>
  3311. # Only readings with containing "SQL_CATALOG_TERM" and "NAME" in name will be shown <br>
  3312. </ul><br>
  3313. <li><b>showTableInfo </b> - limits the sample space of command "get ... tableinfo". SQL-Wildcards (% _) can be used. </li> <br>
  3314. <ul>
  3315. Example: attr ... showTableInfo current,history <br>
  3316. # Only informations about tables "current" and "history" will be shown <br>
  3317. </ul><br>
  3318. <li><b>timestamp_begin </b> - begin of data selection (*) </li> <br>
  3319. <li><b>timestamp_end </b> - end of data selection. If not set the current date/time combination will be used. (*) </li> <br>
  3320. <li><b>timeDiffToNow </b> - the begin of data selection will be set to the timestamp "&lt;current time&gt; - &lt;timeDiffToNow&gt;" dynamically (in seconds). Thereby always the last &lt;timeDiffToNow&gt;-seconds will be considered (e.g. if set to 86400, always the last 24 hours should assumed). The Timestamp calculation will be done dynamically at execution time. </li> <br>
  3321. <li><b>timeOlderThan </b> - the end of data selection will be set to the timestamp "&lt;aktuelle Zeit&gt; - &lt;timeOlderThan&gt;" dynamically (in seconds). Always the datasets up to timestamp "&lt;current time&gt; - &lt;timeOlderThan&gt;" will be considered (e.g. if set to 86400, all datasets older than one day will be considered). The Timestamp calculation will be done dynamically at execution time. </li> <br>
  3322. <li><b>timeout </b> - sets the timeout-value for Blocking-Call Routines in background (default 60 seconds) </li> <br>
  3323. </ul></ul>
  3324. <br>
  3325. (*) The format of timestamp is as used with DbLog "YYYY-MM-DD HH:MM:SS". For the attributes "timestamp_begin", "timestamp_end" you can also use one of: <br><br>
  3326. <ul>
  3327. <b>current_year_begin</b> : set the timestamp-attribute to "&lt;current year&gt;-01-01 00:00:00" dynamically <br>
  3328. <b>current_year_end</b> : set the timestamp-attribute to "&lt;current year&gt;-12-31 23:59:59" dynamically <br>
  3329. <b>previous_year_begin</b> : set the timestamp-attribute to "&lt;previous year&gt;-01-01 00:00:00" dynamically <br>
  3330. <b>previous_year_end</b> : set the timestamp-attribute to "&lt;previous year&gt;-12-31 23:59:59" dynamically <br>
  3331. </ul><br>
  3332. Make sure that timestamp_begin < timestamp_end is fulfilled. <br><br>
  3333. <b>Note </b> <br>
  3334. If the attribute "timeDiffToNow" will be set, the attributes "timestamp_begin" respectively "timestamp_end" will be deleted if they were set before.
  3335. The setting of "timestamp_begin" respectively "timestamp_end" causes the deletion of attribute "timeDiffToNow" if it was set before as well.
  3336. <br><br>
  3337. </ul>
  3338. <a name="DbRepReadings"></a>
  3339. <b>Readings</b>
  3340. <br>
  3341. <ul>
  3342. Regarding to the selected operation the reasults will be shown as readings. At the beginning of a new operation all old readings will be deleted to avoid
  3343. that unsuitable or invalid readings would remain.<br><br>
  3344. In addition the following readings will be created: <br><br>
  3345. <ul><ul>
  3346. <li><b>state </b> - contains the current state of evaluation. If warnings are occured (state = Warning) compare Readings
  3347. "diff_overrun_limit_&lt;diffLimit&gt;" and "less_data_in_period" </li> <br>
  3348. <li><b>errortext </b> - description about the reason of an error state </li> <br>
  3349. <li><b>background_processing_time </b> - the processing time spent for operations in background/forked operation </li> <br>
  3350. <li><b>sql_processing_time </b> - the processing time wasted for all sql-statements used for an operation </li> <br>
  3351. <li><b>diff_overrun_limit_&lt;diffLimit&gt;</b> - contains a list of pairs of datasets which have overrun the threshold (&lt;diffLimit&gt;)
  3352. of calculated difference each other determined by attribute "diffAccept" (default=20). </li> <br>
  3353. <li><b>less_data_in_period</b> - contains a list of time periods within only one dataset was found. The difference calculation considers
  3354. the last value of the aggregation period before the current one. Valid for function "diffValue". </li> <br>
  3355. </ul></ul>
  3356. <br><br>
  3357. </ul>
  3358. <a name="DbRepAutoRename"></a>
  3359. <b>DbRep Agent - automatic change of device names in databases and DbRep-definitions after FHEM "rename" command</b>
  3360. <br>
  3361. <ul>
  3362. By the attribute "role" the role of DbRep-device will be configured. The standard role is "Client". If the role has changed to "Agent", the DbRep device
  3363. react automatically on renaming devices in your FHEM installation. The DbRep device is now called DbRep-Agent. <br><br>
  3364. By the DbRep-Agent the following features are activated when a FHEM-device has being renamed: <br><br>
  3365. <ul><ul>
  3366. <li> in the database connected to the DbRep-Agent (Internal Database) dataset containing the old device name will be searched and renamed to the
  3367. to the new device name in <b>all</b> affected datasets. </li> <br>
  3368. <li> in the DbLog-Device assigned to the DbRep-Agent the definition will be changed to substitute the old device name by the new one. Thereby the logging of
  3369. the renamed device will be going on in the database. </li> <br>
  3370. <li> in other existing DbRep-definitions with Type "Client" a possibly set attribute "device = old device name" will be changed to "device = new device name".
  3371. Because of that, reporting definitions will be kept consistent automatically if devices are renamed in FHEM. </li> <br>
  3372. </ul></ul>
  3373. The following restrictions take place if a DbRep device was changed to an Agent by setting attribute "role" to "Agent". These conditions will be activated
  3374. and checked: <br><br>
  3375. <ul><ul>
  3376. <li> within a FHEM installation only one DbRep-Agent can be configured for every defined DbLog-database. That means, if more than one DbLog-database is present,
  3377. you could define same numbers of DbRep-Agents as well as DbLog-devices are defined. </li> <br>
  3378. <li> after changing to DbRep-Agent role only the set-command "renameDevice" will be available and as well as a reduced set of module specific attributes will be
  3379. permitted. If a DbRep-device of privious type "Client" has changed an Agent, furthermore not permitted attributes will be deleted if set. </li> <br>
  3380. </ul></ul>
  3381. All activities like database changes and changes of other DbRep-definitions will be logged in FHEM Logfile with verbose=3. In order that the renameDevice
  3382. function don't running to timeout set the timeout attribute to an appropriate value, especially if there are databases with huge datasets to evaluate.
  3383. As well as all the other database operations of this module, the autorename operation will be executed nonblocking. <br><br>
  3384. <ul>
  3385. <b>Example </b> of definition of a DbRep-device as an Agent: <br><br>
  3386. <code>
  3387. define Rep.Agent DbRep LogDB <br>
  3388. attr Rep.Agent devStateIcon connected:10px-kreis-gelb .*disconnect:10px-kreis-rot .*done:10px-kreis-gruen <br>
  3389. attr Rep.Agent icon security <br>
  3390. attr Rep.Agent role Agent <br>
  3391. attr Rep.Agent room DbLog <br>
  3392. attr Rep.Agent showproctime 1 <br>
  3393. attr Rep.Agent stateFormat { ReadingsVal("$name","state", undef) eq "running" ? "renaming" : ReadingsVal("$name","state", undef). " &raquo;; ProcTime: ".ReadingsVal("$name","sql_processing_time", undef)." sec"} <br>
  3394. attr Rep.Agent timeout 3600 <br>
  3395. </code>
  3396. <br>
  3397. </ul>
  3398. </ul>
  3399. =end html
  3400. =begin html_DE
  3401. <a name="DbRep"></a>
  3402. <h3>DbRep</h3>
  3403. <ul>
  3404. <br>
  3405. Zweck des Moduls ist es, den Inhalt von DbLog-Datenbanken nach bestimmten Kriterien zu durchsuchen, zu managen, das Ergebnis hinsichtlich verschiedener
  3406. Aggregationen auszuwerten und als Readings darzustellen. Die Abgrenzung der zu berücksichtigenden Datenbankinhalte erfolgt durch die Angabe von Device, Reading und
  3407. die Zeitgrenzen für Auswertungsbeginn bzw. Auswertungsende. <br><br>
  3408. Alle Datenbankoperationen werden nichtblockierend ausgeführt. Die Ausführungszeit der (SQL)-Hintergrundoperationen kann optional ebenfalls als Reading bereitgestellt
  3409. werden (siehe <a href="#DbRepattr">Attribute</a>). <br>
  3410. Alle vorhandenen Readings werden vor einer neuen Operation gelöscht. Durch das Attribut "readingPreventFromDel" kann eine Komma separierte Liste von Readings
  3411. angegeben werden die nicht gelöscht werden sollen. <br><br>
  3412. Zur Zeit werden folgende Operationen unterstützt: <br><br>
  3413. <ul><ul>
  3414. <li> Selektion aller Datensätze innerhalb einstellbarer Zeitgrenzen. </li>
  3415. <li> Darstellung der Datensätze einer Device/Reading-Kombination innerhalb einstellbarer Zeitgrenzen. </li>
  3416. <li> Selektion der Datensätze unter Verwendung von dynamisch berechneter Zeitgrenzen zum Ausführungszeitpunkt. </li>
  3417. <li> Berechnung der Anzahl von Datensätzen einer Device/Reading-Kombination unter Berücksichtigung von Zeitgrenzen und verschiedenen Aggregationen. </li>
  3418. <li> Die Berechnung von Summen- , Differenz- , Maximum- , Minimum- und Durchschnittswerten von numerischen Readings in Zeitgrenzen und verschiedenen Aggregationen. </li>
  3419. <li> Löschung von Datensätzen. Die Eingrenzung der Löschung kann durch Device und/oder Reading sowie fixer oder dynamisch berechneter Zeitgrenzen zum Ausführungszeitpunkt erfolgen. </li>
  3420. <li> Export von Datensätzen in ein File im CSV-Format </li>
  3421. <li> Import von Datensätzen aus File im CSV-Format </li>
  3422. <li> Umbenennen von Device-Namen in Datenbanksätzen </li>
  3423. <li> automatisches Umbenennen von Device-Namen in Datenbanksätzen und DbRep-Definitionen nach FHEM "rename" Befehl (siehe <a href="#DbRepAutoRename">DbRep-Agent</a>) </li>
  3424. </ul></ul>
  3425. <br>
  3426. Zur Aktivierung der Funktion "Autorename" wird dem definierten DbRep-Device mit dem Attribut "role" die Rolle "Agent" zugewiesen. Die Standardrolle nach Definition
  3427. ist "Client". Mehr ist dazu im Abschnitt <a href="#DbRepAutoRename">DbRep-Agent</a> beschrieben. <br><br>
  3428. FHEM-Forum: <br>
  3429. <a href="https://forum.fhem.de/index.php/topic,53584.msg452567.html#msg452567">Modul 93_DbRep - Reporting und Management von Datenbankinhalten (DbLog)</a>.<br><br>
  3430. <b>Voraussetzungen </b> <br><br>
  3431. Das Modul setzt den Einsatz einer oder mehrerer DBLog-Instanzen voraus. Es werden die Zugangsdaten dieser Datenbankdefinition genutzt (bisher getestet mit MySQL und SQLite). <br>
  3432. Es werden nur Inhalte der Tabelle "history" berücksichtigt. <br><br>
  3433. Überblick welche anderen Perl-Module DbRep verwendet: <br><br>
  3434. POSIX <br>
  3435. Time::HiRes <br>
  3436. Time::Local <br>
  3437. Scalar::Util <br>
  3438. DBI <br>
  3439. Blocking (FHEM-Modul) <br><br>
  3440. Aus Performancegründen sollten zusätzlich folgender Index erstellt werden: <br>
  3441. <code>
  3442. ALTER TABLE 'fhem'.'history' ADD INDEX `Reading_Time_Idx` (`READING`, `TIMESTAMP`) USING BTREE;
  3443. </code>
  3444. </ul>
  3445. <br>
  3446. <a name="DbRepdefine"></a>
  3447. <b>Definition</b>
  3448. <br>
  3449. <ul>
  3450. <code>
  3451. define &lt;name&gt; DbRep &lt;Name der DbLog-instanz&gt;
  3452. </code>
  3453. <br><br>
  3454. (&lt;Name der DbLog-instanz&gt; - es wird der Name der auszuwertenden DBLog-Datenbankdefinition angegeben <b>nicht</b> der Datenbankname selbst)
  3455. </ul>
  3456. <br><br>
  3457. <a name="DbRepset"></a>
  3458. <b>Set </b>
  3459. <ul>
  3460. Zur Zeit gibt es folgende Set-Kommandos. Über sie werden die Auswertungen angestoßen und definieren selbst die Auswertungsvariante.
  3461. Nach welchen Kriterien die Datenbankinhalte durchsucht werden und die Aggregation erfolgt, wird durch <a href="#DbRepattr">Attribute</a> gesteuert.
  3462. <br><br>
  3463. <ul><ul>
  3464. <li><b> averageValue </b> - berechnet den Durchschnittswert der Readingwerte (DB-Spalte "VALUE") in den gegebenen Zeitgrenzen ( siehe <a href="#DbRepattr">Attribute</a>).
  3465. Es muss das auszuwertende Reading über das <a href="#DbRepattr">Attribut</a> "reading" angegeben sein. </li> <br>
  3466. <li><b> countEntries </b> - liefert die Anzahl der DB-Einträge in den gegebenen Zeitgrenzen (siehe <a href="#DbRepattr">Attribute</a>).
  3467. Sind die Timestamps nicht gesetzt werden alle Einträge gezählt.
  3468. Beschränkungen durch die <a href="#DbRepattr">Attribute</a> Device bzw. Reading gehen in die Selektion mit ein. </li> <br>
  3469. <li><b> deviceRename </b> - benennt den Namen eines Device innerhalb der angeschlossenen Datenbank (Internal DATABASE) um.
  3470. Der Gerätename wird immer in der <b>gesamten</b> Datenbank umgesetzt. Eventuell gesetzte Zeitgrenzen oder Beschränkungen
  3471. durch die <a href="#DbRepattr">Attribute</a> Device bzw. Reading werden nicht berücksichtigt. <br><br>
  3472. <ul>
  3473. <b>Eingabeformat: </b> set &lt;name&gt; deviceRename &lt;alter Devicename&gt;,&lt;neuer Devicename&gt; <br>
  3474. # Die Anzahl der umbenannten Device-Datensätze wird im Reading "device_renamed" ausgegeben. <br>
  3475. # Wird der umzubenennende Gerätename in der Datenbank nicht gefunden, wird eine WARNUNG im Reading "device_not_renamed" ausgegeben. <br>
  3476. # Entsprechende Einträge erfolgen auch im Logfile mit verbose=3
  3477. <br><br>
  3478. </li> <br>
  3479. </ul>
  3480. <li><b> readingRename </b> - benennt den Namen eines Readings innerhalb der angeschlossenen Datenbank (siehe Internal DATABASE) um.
  3481. Der Readingname wird immer in der <b>gesamten</b> Datenbank umgesetzt. Eventuell gesetzte Zeitgrenzen oder Beschränkungen
  3482. durch die <a href="#DbRepattr">Attribute</a> Device bzw. Reading werden nicht berücksichtigt. <br><br>
  3483. <ul>
  3484. <b>Eingabeformat: </b> set &lt;name&gt; readingRename &lt;alter Readingname&gt;,&lt;neuer Readingname&gt; <br>
  3485. # Die Anzahl der umbenannten Device-Datensätze wird im Reading "reading_renamed" ausgegeben. <br>
  3486. # Wird der umzubenennende Readingname in der Datenbank nicht gefunden, wird eine WARNUNG im Reading "reading_not_renamed" ausgegeben. <br>
  3487. # Entsprechende Einträge erfolgen auch im Logfile mit verbose=3.
  3488. <br><br>
  3489. </li> <br>
  3490. </ul>
  3491. <li><b> exportToFile </b> - exportiert DB-Einträge im CSV-Format in den gegebenen Zeitgrenzen.
  3492. Einschränkungen durch die <a href="#DbRepattr">Attribute</a> Device bzw. Reading gehen in die Selektion mit ein.
  3493. Der Filename wird durch das <a href="#DbRepattr">Attribut</a> "expimpfile" bestimmt. </li> <br>
  3494. <li><b> fetchrows </b> - liefert <b>alle</b> DB-Einträge in den gegebenen Zeitgrenzen ( siehe <a href="#DbRepattr">Attribute</a>).
  3495. Eine evtl. gesetzte Aggregation wird <b>nicht</b> berücksichtigt. </li> <br>
  3496. <li><b> insert </b> - Manuelles Einfügen eines Datensatzes in die Tabelle "history". Obligatorisch sind Eingabewerte für Datum, Zeit und Value.
  3497. Die Werte für die DB-Felder Type bzw. Event werden mit "manual" gefüllt, sowie die Werte für Device, Reading aus den gesetzten <a href="#DbRepattr">Attributen </a> genommen. <br><br>
  3498. <ul>
  3499. <b>Eingabeformat: </b> Datum,Zeit,Value,[Unit] <br>
  3500. # Unit ist optional, Attribute "reading" und "device" müssen gesetzt sein <br>
  3501. # Soll "Value=0" eingefügt werden, ist "Value = 0.0" zu verwenden. <br><br>
  3502. <b>Beispiel: </b> 2016-08-01,23:00:09,TestValue,TestUnit <br>
  3503. # die Feldlänge ist maximal 64 Zeichen lang , es sind KEINE Leerzeichen im Feldwert erlaubt !<br>
  3504. <br>
  3505. <b>Hinweis: </b><br>
  3506. Bei der Eingabe ist darauf zu achten dass im beabsichtigten Aggregationszeitraum (Tag, Woche, Monat, etc.) MINDESTENS zwei
  3507. Datensätze für die Funktion diffValue zur Verfügung stehen. Ansonsten kann keine Differenz berechnet werden und diffValue
  3508. gibt in diesem Fall "0" in der betroffenen Periode aus !
  3509. <br>
  3510. <br>
  3511. </li>
  3512. </ul>
  3513. <li><b> importFromFile </b> - importiert Datensätze im CSV-Format aus einem File in die Datenbank. Der Filename wird durch das <a href="#DbRepattr">Attribut</a> "expimpfile" bestimmt. <br><br>
  3514. <ul>
  3515. <b>Datensatzformat: </b> "TIMESTAMP","DEVICE","TYPE","EVENT","READING","VALUE","UNIT" <br><br>
  3516. # Die Felder "TIMESTAMP","DEVICE","TYPE","EVENT","READING" und "VALUE" müssen gesetzt sein. Das Feld "UNIT" ist optional.
  3517. Der Fileinhalt wird als Transaktion importiert, d.h. es wird der Inhalt des gesamten Files oder, im Fehlerfall, kein Datensatz des Files importiert.
  3518. Wird eine umfangreiche Datei mit vielen Datensätzen importiert sollte KEIN verbose=5 gesetzt werden. Es würden in diesem Fall sehr viele Sätze in
  3519. das Logfile geschrieben werden was FHEM blockieren oder überlasten könnte. <br><br>
  3520. <b>Beispiel: </b> "2016-09-25 08:53:56","STP_5000","SMAUTILS","etotal: 11859.573","etotal","11859.573","" <br>
  3521. <br>
  3522. </li> <br>
  3523. </ul>
  3524. <li><b> sumValue </b> - berechnet die Summenwerte eines Readingwertes (DB-Spalte "VALUE") in den Zeitgrenzen (Attribute) "timestamp_begin", "timestamp_end" bzw. "timeDiffToNow / timeOlderThan".
  3525. Es muss das auszuwertende Reading im <a href="#DbRepattr">Attribut</a> "reading" angegeben sein.
  3526. Diese Funktion ist sinnvoll wenn fortlaufend Wertedifferenzen eines Readings in die Datenbank geschrieben werden. </li> <br>
  3527. <li><b> maxValue </b> - berechnet den Maximalwert eines Readingwertes (DB-Spalte "VALUE") in den Zeitgrenzen (Attribute) "timestamp_begin", "timestamp_end" bzw. "timeDiffToNow / timeOlderThan".
  3528. Es muss das auszuwertende Reading über das <a href="#DbRepattr">Attribut</a> "reading" angegeben sein.
  3529. Die Auswertung enthält den Zeitstempel des ermittelten Maximumwertes innerhalb der Aggregation bzw. Zeitgrenzen.
  3530. Im Reading wird der Zeitstempel des <b>letzten</b> Auftretens vom Maximalwert ausgegeben falls dieser Wert im Intervall mehrfach erreicht wird. </li> <br>
  3531. <li><b> minValue </b> - berechnet den Minimalwert eines Readingwertes (DB-Spalte "VALUE") in den Zeitgrenzen (Attribute) "timestamp_begin", "timestamp_end" bzw. "timeDiffToNow / timeOlderThan".
  3532. Es muss das auszuwertende Reading über das <a href="#DbRepattr">Attribut</a> "reading" angegeben sein.
  3533. Die Auswertung enthält den Zeitstempel des ermittelten Minimumwertes innerhalb der Aggregation bzw. Zeitgrenzen.
  3534. Im Reading wird der Zeitstempel des <b>ersten</b> Auftretens vom Minimalwert ausgegeben falls dieser Wert im Intervall mehrfach erreicht wird. </li> <br>
  3535. <li><b> diffValue </b> - berechnet den Differenzwert eines Readingwertes (DB-Spalte "Value") in den Zeitgrenzen (Attribute) "timestamp_begin", "timestamp_end" bzw "timeDiffToNow / timeOlderThan".
  3536. Es muss das auszuwertende Reading im Attribut "reading" angegeben sein.
  3537. Diese Funktion ist z.B. zur Auswertung von Eventloggings sinnvoll, deren Werte sich fortlaufend erhöhen und keine Wertdifferenzen wegschreiben. <br>
  3538. Es wird immer die Differenz aus dem Value-Wert des ersten verfügbaren Datensatzes und dem Value-Wert des letzten verfügbaren Datensatzes innerhalb der angegebenen
  3539. Zeitgrenzen/Aggregation gebildet, wobei ein Übertragswert der Vorperiode (Aggregation) zur darauf folgenden Aggregationsperiode
  3540. berücksichtigt wird sofern diese einen Value-Wert enhtält. <br>
  3541. Dabei wird ein Zählerüberlauf (Neubeginn bei 0) mit berücksichtigt (vergleiche <a href="#DbRepattr">Attribut</a> "diffAccept"). <br>
  3542. Wird in einer auszuwertenden Zeit- bzw. Aggregationsperiode nur ein Datensatz gefunden, kann die Differenz in Verbindung mit dem
  3543. Differenzübertrag der Vorperiode berechnet werden. in diesem Fall kann es zu einer logischen Ungenauigkeit in der Zuordnung der Differenz
  3544. zu der Aggregationsperiode kommen. Deswegen wird eine Warnung im "state" und das
  3545. Reading "less_data_in_period" mit einer Liste der betroffenen Perioden wird erzeugt. <br><br>
  3546. <ul>
  3547. <b>Hinweis: </b><br>
  3548. Im Auswertungs- bzw. Aggregationszeitraum (Tag, Woche, Monat, etc.) sollten dem Modul pro Periode mindestens ein Datensatz
  3549. zu Beginn und ein Datensatz gegen Ende des Aggregationszeitraumes zur Verfügung stehen um eine möglichst genaue Auswertung
  3550. der Differenzwerte vornehmen zu können.
  3551. <br>
  3552. <br>
  3553. </li>
  3554. </ul>
  3555. <li><b> delEntries </b> - löscht alle oder die durch die <a href="#DbRepattr">Attribute</a> device und/oder reading definierten Datenbankeinträge. Die Eingrenzung über Timestamps erfolgt folgendermaßen: <br><br>
  3556. <ul>
  3557. "timestamp_begin" gesetzt: gelöscht werden DB-Einträge <b>ab</b> diesem Zeitpunkt bis zum aktuellen Datum/Zeit <br>
  3558. "timestamp_end" gesetzt : gelöscht werden DB-Einträge <b>bis</b> bis zu diesem Zeitpunkt <br>
  3559. beide Timestamps gesetzt : gelöscht werden DB-Einträge <b>zwischen</b> diesen Zeitpunkten <br>
  3560. <br>
  3561. Aus Sicherheitsgründen muss das <a href="#DbRepattr">Attribut</a> "allowDeletion" gesetzt sein um die Löschfunktion freizuschalten. <br>
  3562. </li>
  3563. </ul>
  3564. <br>
  3565. </ul></ul>
  3566. <b>Für alle Auswertungsvarianten gilt: </b> <br>
  3567. Zusätzlich zu dem auszuwertenden Reading kann das Device mit angegeben werden um das Reporting nach diesen Kriterien einzuschränken.
  3568. Sind keine Zeitgrenzen-Attribute angegeben, wird '1970-01-01 01:00:00' und das aktuelle Datum/Zeit als Zeitgrenze genutzt.
  3569. <br><br>
  3570. <b>Hinweis: </b> <br>
  3571. In der Detailansicht kann ein Browserrefresh nötig sein um die Operationsergebnisse zu sehen sobald im DeviceOverview "state = done" angezeigt wird.
  3572. <br><br>
  3573. </ul>
  3574. <a name="DbRepget"></a>
  3575. <b>Get </b>
  3576. <ul>
  3577. Die Get-Kommandos von DbRep dienen dazu eine Reihe von Metadaten der verwendeten Datenbankinstanz abzufragen.
  3578. Dies sind zum Beispiel eingestellte Serverparameter, Servervariablen, Datenbankstatus- und Tabelleninformationen. Die verfügbaren get-Funktionen
  3579. sind von dem verwendeten Datenbanktyp abhängig. So ist für SQLite z.Zt. nur "svrinfo" verfügbar. Die Funktionen liefern nativ sehr viele Ausgabewerte,
  3580. die über über funktionsspezifische <a href="#DbRepattr">Attribute</a> abgrenzbar sind. Der Filter ist als kommaseparierte Liste anzuwenden.
  3581. Dabei können SQL-Wildcards (% _) verwendet werden.
  3582. <br><br>
  3583. <b>Hinweis: </b> <br>
  3584. Nach der Ausführung einer get-Funktion in der Detailsicht einen Browserrefresh durchführen um die Ergebnisse zu sehen !
  3585. <br><br>
  3586. <ul><ul>
  3587. <li><b> dbstatus </b> - listet globale Informationen zum MySQL Serverstatus (z.B. Informationen zum Cache, Threads, Bufferpools, etc. ).
  3588. Es werden zunächst alle verfügbaren Informationen berichtet. Mit dem <a href="#DbRepattr">Attribut</a> "showStatus" kann die
  3589. Ergebnismenge eingeschränkt werden, um nur gewünschte Ergebnisse abzurufen. Detailinformationen zur Bedeutung der einzelnen Readings
  3590. sind <a href=http://dev.mysql.com/doc/refman/5.7/en/server-status-variables.html>hier</a> verfügbar. <br>
  3591. <br><ul>
  3592. Bespiel: <br>
  3593. get &lt;name&gt; dbstatus <br>
  3594. attr &lt;name&gt; showStatus %uptime%,%qcache% <br>
  3595. # Es werden nur Readings erzeugt die im Namen "uptime" und "qcache" enthalten
  3596. </li>
  3597. <br><br>
  3598. </ul>
  3599. <li><b> dbvars </b> - zeigt die globalen Werte der MySQL Systemvariablen. Enthalten sind zum Beispiel Angaben zum InnoDB-Home, dem Datafile-Pfad,
  3600. Memory- und Cache-Parameter, usw. Die Ausgabe listet zunächst alle verfügbaren Informationen auf. Mit dem
  3601. <a href="#DbRepattr">Attribut</a> "showVariables" kann die Ergebnismenge eingeschränkt werden um nur gewünschte Ergebnisse
  3602. abzurufen. Weitere Informationen zur Bedeutung der ausgegebenen Variablen sind
  3603. <a href=http://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html>hier</a> verfügbar. <br>
  3604. <br><ul>
  3605. Bespiel: <br>
  3606. get &lt;name&gt; dbvars <br>
  3607. attr &lt;name&gt; showVariables %version%,%query_cache% <br>
  3608. # Es werden nur Readings erzeugt die im Namen "version" und "query_cache" enthalten
  3609. </li>
  3610. <br><br>
  3611. </ul>
  3612. <li><b> svrinfo </b> - allgemeine Datenbankserver-Informationen wie z.B. die DBMS-Version, Serveradresse und Port usw. Die Menge der Listenelemente
  3613. ist vom Datenbanktyp abhängig. Mit dem <a href="#DbRepattr">Attribut</a> "showSvrInfo" kann die Ergebnismenge eingeschränkt werden.
  3614. Weitere Erläuterungen zu den gelieferten Informationen sind
  3615. <a href=https://msdn.microsoft.com/en-us/library/ms711681(v=vs.85).aspx>hier</a> zu finden. <br>
  3616. <br><ul>
  3617. Bespiel: <br>
  3618. get &lt;name&gt; svrinfo <br>
  3619. attr &lt;name&gt; showSvrInfo %SQL_CATALOG_TERM%,%NAME% <br>
  3620. # Es werden nur Readings erzeugt die im Namen "SQL_CATALOG_TERM" und "NAME" enthalten
  3621. </li>
  3622. <br><br>
  3623. </ul>
  3624. <li><b> tableinfo </b> - ruft Detailinformationen der in einem MySQL-Schema angelegten Tabellen ab. Die ausgewerteten Schemata sind abhängig von den Rechten
  3625. des verwendeten Datenbankusers (default: das DB-Schema der current/history-Tabelle).
  3626. Mit dem <a href="#DbRepattr">Attribut</a> "showTableInfo" können die Ergebnisse eingeschränkt werden. Erläuterungen zu den erzeugten
  3627. Readings sind <a href=http://dev.mysql.com/doc/refman/5.7/en/show-table-status.html>hier</a> zu finden. <br>
  3628. <br><ul>
  3629. Bespiel: <br>
  3630. get &lt;name&gt; tableinfo <br>
  3631. attr &lt;name&gt; showTableInfo current,history <br>
  3632. # Es werden nur Information der Tabellen "current" und "history" angezeigt
  3633. </li>
  3634. <br><br>
  3635. </ul>
  3636. <br>
  3637. </ul></ul>
  3638. </ul>
  3639. <a name="DbRepattr"></a>
  3640. <b>Attribute</b>
  3641. <br>
  3642. <ul>
  3643. Über die modulspezifischen Attribute wird die Abgrenzung der Auswertung und die Aggregation der Werte gesteuert. <br><br>
  3644. <b>Hinweis zur SQL-Wildcard Verwendung:</b> <br>
  3645. Innerhalb der Attribut-Werte für "device" und "reading" können SQL-Wildcards, "%" und "_", angegeben werden. Dabei ist "%" = beliebig
  3646. viele Zeichen und "_" = ein Zeichen. <br>
  3647. Dies gilt für alle Funktionen <b>außer</b> "insert", "deviceRename" und "delEntries". <br>
  3648. Die Funktion "insert" erlaubt nicht dass die genannten Attribute das Wildcard "%" enthalten, "_" wird als normales Zeichen gewertet.<br>
  3649. Die Löschfunktion "delEntries" wertet die Zeichen "$", "_" <b>NICHT</b> als Wildcards und löscht nur Device/Readings die exakt wie in den Attributen angegeben
  3650. in der DB gespeichert sind. <br>
  3651. In den Readings wird das Wildcardzeichen "%" durch "/" ersetzt um die Regeln für erlaubte Zeichen in Readings einzuhalten.
  3652. <br><br>
  3653. <ul><ul>
  3654. <li><b>aggregation </b> - Zusammenfassung der Device/Reading-Selektionen in Stunden,Tages,Kalenderwochen,Kalendermonaten oder "no". Liefert z.B. die Anzahl der DB-Einträge am Tag (countEntries), Summation von Differenzwerten eines Readings (sumValue), usw. Mit Aggregation "no" (default) erfolgt keine Zusammenfassung in einem Zeitraum sondern die Ausgabe ergibt alle Werte eines Device/Readings zwischen den definierten Zeiträumen. </li> <br>
  3655. <li><b>allowDeletion </b> - schaltet die Löschfunktion des Moduls frei </li> <br>
  3656. <li><b>device </b> - Abgrenzung der DB-Selektionen auf ein bestimmtes Device. </li> <br>
  3657. <li><b>diffAccept </b> - gilt für Funktion diffValue. diffAccept legt fest bis zu welchem Schwellenwert eine berechnete positive Werte-Differenz
  3658. zwischen zwei unmittelbar aufeinander folgenden Datensätzen akzeptiert werden soll (Standard ist 20). <br>
  3659. Damit werden fehlerhafte DB-Einträge mit einem unverhältnismäßig hohen Differenzwert von der Berechnung ausgeschlossen und
  3660. verfälschen nicht das Ergebnis. Sollten Schwellenwertüberschreitungen vorkommen, wird das Reading "diff_overrun_limit_&lt;diffLimit&gt;"
  3661. erstellt. (&lt;diffLimit&gt; wird dabei durch den aktuellen Attributwert ersetzt)
  3662. Es enthält eine Liste der relevanten Wertepaare. Mit verbose 3 werden diese Datensätze ebenfalls im Logfile protokolliert.
  3663. </li> <br>
  3664. <ul>
  3665. Beispiel Ausgabe im Logfile beim Überschreiten von diffAccept=10: <br><br>
  3666. DbRep Rep.STP5000.etotal -> data ignored while calc diffValue due to threshold overrun (diffAccept = 10): <br>
  3667. 2016-04-09 08:50:50 0.0340 -> 2016-04-09 12:42:01 13.3440 <br><br>
  3668. # Der erste Datensatz mit einem Wert von 0.0340 ist untypisch gering zum nächsten Wert 13.3440 und führt zu einem zu hohen
  3669. Differenzwert. <br>
  3670. # Es ist zu entscheiden ob der Datensatz gelöscht, ignoriert, oder das Attribut diffAccept angepasst werden sollte.
  3671. </ul><br>
  3672. <li><b>disable </b> - deaktiviert das Modul </li> <br>
  3673. <li><b>expimpfile </b> - Pfad/Dateiname für Export/Import in/aus einem File. </li> <br>
  3674. <li><b>reading </b> - Abgrenzung der DB-Selektionen auf ein bestimmtes Reading </li> <br>
  3675. <li><b>readingNameMap </b> - der Name des ausgewerteten Readings wird mit diesem String für die Anzeige überschrieben </li> <br>
  3676. <li><b>readingPreventFromDel </b> - Komma separierte Liste von Readings die vor einer neuen Operation nicht gelöscht werden sollen </li> <br>
  3677. <li><b>role </b> - die Rolle des DbRep-Device. Standard ist "Client". Die Rolle "Agent" ist im Abschnitt <a href="#DbRepAutoRename">DbRep-Agent</a> beschrieben. </li> <br>
  3678. <li><b>showproctime </b> - wenn gesetzt, zeigt das Reading "sql_processing_time" die benötigte Abarbeitungszeit (in Sekunden) für die SQL-Ausführung der durchgeführten Funktion. Dabei wird nicht ein einzelnes SQl-Statement, sondern die Summe aller notwendigen SQL-Abfragen innerhalb der jeweiligen Funktion betrachtet. </li> <br>
  3679. <li><b>showStatus </b> - grenzt die Ergebnismenge des Befehls "get ... dbstatus" ein. Es können SQL-Wildcards (% _) verwendet werden. </li> <br>
  3680. <ul>
  3681. Bespiel: attr ... showStatus %uptime%,%qcache% <br>
  3682. # Es werden nur Readings erzeugt die im Namen "uptime" und "qcache" enthalten <br>
  3683. </ul><br>
  3684. <li><b>showVariables </b> - grenzt die Ergebnismenge des Befehls "get ... dbvars" ein. Es können SQL-Wildcards (% _) verwendet werden. </li> <br>
  3685. <ul>
  3686. Bespiel: attr ... showVariables %version%,%query_cache% <br>
  3687. # Es werden nur Readings erzeugt die im Namen "version" und "query_cache" enthalten <br>
  3688. </ul><br>
  3689. <li><b>showSvrInfo </b> - grenzt die Ergebnismenge des Befehls "get ... svrinfo" ein. Es können SQL-Wildcards (% _) verwendet werden. </li> <br>
  3690. <ul>
  3691. Bespiel: attr ... showSvrInfo %SQL_CATALOG_TERM%,%NAME% <br>
  3692. # Es werden nur Readings erzeugt die im Namen "SQL_CATALOG_TERM" und "NAME" enthalten <br>
  3693. </ul><br>
  3694. <li><b>showTableInfo </b> - grenzt die Ergebnismenge des Befehls "get ... tableinfo" ein. Es können SQL-Wildcards (% _) verwendet werden. </li> <br>
  3695. <ul>
  3696. Bespiel: attr ... showTableInfo current,history <br>
  3697. # Es werden nur Information der Tabellen "current" und "history" angezeigt <br>
  3698. </ul><br>
  3699. <li><b>timestamp_begin </b> - der zeitliche Beginn für die Datenselektion (*) </li> <br>
  3700. <li><b>timestamp_end </b> - das zeitliche Ende für die Datenselektion. Wenn nicht gesetzt wird immer die aktuelle Datum/Zeit-Kombi für das Ende der Selektion eingesetzt. (*) </li> <br>
  3701. <li><b>timeDiffToNow </b> - der Selektionsbeginn wird auf den Zeitpunkt "&lt;aktuelle Zeit&gt; - &lt;timeDiffToNow&gt;" gesetzt (in Sekunden). Es werden immer die letzten &lt;timeDiffToNow&gt;-Sekunden berücksichtigt (z.b. 86400 wenn immer die letzten 24 Stunden in die Selektion eingehen sollen). Die Timestampermittlung erfolgt dynamisch zum Ausführungszeitpunkt. </li> <br>
  3702. <li><b>timeOlderThan </b> - das Selektionsende wird auf den Zeitpunkt "&lt;aktuelle Zeit&gt; - &lt;timeOlderThan&gt;" gesetzt (in Sekunden). Dadurch werden alle Datensätze bis zu dem Zeitpunkt "&lt;aktuelle Zeit&gt; - &lt;timeOlderThan&gt;" berücksichtigt (z.b. wenn auf 86400 gesetzt werden alle Datensätze die älter als ein Tag sind berücksichtigt). Die Timestampermittlung erfolgt dynamisch zum Ausführungszeitpunkt. </li> <br>
  3703. <li><b>timeout </b> - das Attribut setzt den Timeout-Wert für die Blocking-Call Routinen (Standard 60) in Sekunden </li> <br>
  3704. </ul></ul>
  3705. <br>
  3706. (*) Das Format von Timestamp ist wie in DbLog "YYYY-MM-DD HH:MM:SS". Für die Attribute "timestamp_begin", "timestamp_end" kann ebenso eine der folgenden Eingaben verwendet werden: <br><br>
  3707. <ul>
  3708. <b>current_year_begin</b> : belegt das timestamp-Attribut dynamisch mit "&lt;aktuelles Jahr&gt;-01-01 00:00:00" <br>
  3709. <b>current_year_end</b> : belegt das timestamp-Attribut dynamisch mit "&lt;aktuelles Jahr&gt;-12-31 23:59:59" <br>
  3710. <b>previous_year_begin</b> : belegt das timestamp-Attribut dynamisch mit "&lt;voriges Jahr&gt;-01-01 00:00:00" <br>
  3711. <b>previous_year_end</b> : belegt das timestamp-Attribut dynamisch mit "&lt;voriges Jahr&gt;-12-31 23:59:59" <br>
  3712. </ul><br>
  3713. Natürlich sollte man immer darauf achten dass timestamp_begin < timestamp_end ist. <br><br>
  3714. <b>Hinweis </b> <br>
  3715. Wird das Attribut "timeDiffToNow" gesetzt, werden die evtentuell gesetzten Attribute "timestamp_begin" bzw. "timestamp_end" gelöscht.
  3716. Das Setzen von "timestamp_begin" bzw. "timestamp_end" bedingt die Löschung von Attribut "timeDiffToNow" wenn es vorher gesetzt war.
  3717. <br><br>
  3718. </ul>
  3719. <a name="DbRepReadings"></a>
  3720. <b>Readings</b>
  3721. <br>
  3722. <ul>
  3723. Abhängig von der ausgeführten DB-Operation werden die Ergebnisse in entsrechenden Readings dargestellt. Zu Beginn einer neuen Operation werden alle alten Readings
  3724. einer vorangegangenen Operation gelöscht um den Verbleib unpassender bzw. ungültiger Readings zu vermeiden. <br><br>
  3725. Zusätzlich werden folgende Readings erzeugt: <br><br>
  3726. <ul><ul>
  3727. <li><b>state </b> - enthält den aktuellen Status der Auswertung. Wenn Warnungen auftraten (state = Warning) vergleiche Readings
  3728. "diff_overrun_limit_&lt;diffLimit&gt;" und "less_data_in_period" </li> <br>
  3729. <li><b>errortext </b> - Grund eines Fehlerstatus </li> <br>
  3730. <li><b>background_processing_time </b> - die gesamte Prozesszeit die im Hintergrund/Blockingcall verbraucht wird </li> <br>
  3731. <li><b>sql_processing_time </b> - der Anteil der Prozesszeit die für alle SQL-Statements der ausgeführten Operation verbraucht wird </li> <br>
  3732. <li><b>diff_overrun_limit_&lt;diffLimit&gt;</b> - enthält eine Liste der Wertepaare die eine durch das Attribut "diffAccept" festgelegte Differenz
  3733. &lt;diffLimit&gt; (Standard: 20) überschreiten. Gilt für Funktion "diffValue". </li> <br>
  3734. <li><b>less_data_in_period</b> - enthält eine Liste der Zeitperioden in denen nur ein einziger Datensatz gefunden wurde. Die
  3735. Differenzberechnung berücksichtigt den letzten Wert der Vorperiode. Gilt für Funktion "diffValue". </li> <br>
  3736. </ul></ul>
  3737. <br>
  3738. </ul>
  3739. <a name="DbRepAutoRename"></a>
  3740. <b>DbRep Agent - automatisches Ändern von Device-Namen in Datenbanken und DbRep-Definitionen nach FHEM "rename" Kommando</b>
  3741. <br>
  3742. <ul>
  3743. Mit dem Attribut "role" wird die Rolle des DbRep-Device festgelegt. Die Standardrolle ist "Client". Mit der Änderung der Rolle in "Agent" wird das Device
  3744. veranlasst auf Umbenennungen von Geräten in der FHEM Installation zu reagieren. <br><br>
  3745. Durch den DbRep-Agenten werden folgende Features aktiviert wenn ein Gerät in FHEM mit "rename" umbenannt wird: <br><br>
  3746. <ul><ul>
  3747. <li> in der dem DbRep-Agenten zugeordneten Datenbank (Internal Database) wird nach Datensätzen mit dem alten Gerätenamen gesucht und dieser Gerätename in
  3748. <b>allen</b> betroffenen Datensätzen in den neuen Namen geändert. </li> <br>
  3749. <li> in dem DbRep-Agenten zugeordneten DbLog-Device wird in der Definition das alte durch das umbenannte Device ersetzt. Dadurch erfolgt ein weiteres Logging
  3750. des umbenannten Device in der Datenbank. </li> <br>
  3751. <li> in den existierenden DbRep-Definitionen vom Typ "Client" wird ein evtl. gesetztes Attribut "device = alter Devicename" in "device = neuer Devicename"
  3752. geändert. Dadurch werden Auswertungsdefinitionen bei Geräteumbenennungen automatisch konstistent gehalten. </li> <br>
  3753. </ul></ul>
  3754. Mit der Änderung in einen Agenten sind folgende Restriktionen verbunden die mit dem Setzen des Attributes "role = Agent" eingeschaltet
  3755. und geprüft werden: <br><br>
  3756. <ul><ul>
  3757. <li> es kann nur einen Agenten pro Datenbank in der FHEM-Installation geben. Ist mehr als eine Datenbank mit DbLog definiert, können
  3758. ebenso viele DbRep-Agenten eingerichtet werden </li> <br>
  3759. <li> mit der Umwandlung in einen Agenten wird nur noch das Set-Komando "renameDevice" verfügbar sein sowie nur ein eingeschränkter Satz von DbRep-spezifischen
  3760. Attributen zugelassen. Wird ein DbRep-Device vom bisherigen Typ "Client" in einen Agenten geändert, werden evtl. gesetzte und nun nicht mehr zugelassene
  3761. Attribute glöscht. </li> <br>
  3762. </ul></ul>
  3763. Die Aktivitäten wie Datenbankänderungen bzw. Änderungen an anderen DbRep-Definitionen werden im Logfile mit verbose=3 protokolliert. Damit die renameDevice-Funktion
  3764. bei großen Datenbanken nicht in ein timeout läuft, sollte das Attribut "timeout" entsprechend dimensioniert werden. Wie alle Datenbankoperationen des Moduls
  3765. wird auch das Autorename nonblocking ausgeführt. <br><br>
  3766. <ul>
  3767. <b>Beispiel </b> für die Definition eines DbRep-Device als Agent: <br><br>
  3768. <code>
  3769. define Rep.Agent DbRep LogDB <br>
  3770. attr Rep.Agent devStateIcon connected:10px-kreis-gelb .*disconnect:10px-kreis-rot .*done:10px-kreis-gruen <br>
  3771. attr Rep.Agent icon security <br>
  3772. attr Rep.Agent role Agent <br>
  3773. attr Rep.Agent room DbLog <br>
  3774. attr Rep.Agent showproctime 1 <br>
  3775. attr Rep.Agent stateFormat { ReadingsVal("$name","state", undef) eq "running" ? "renaming" : ReadingsVal("$name","state", undef). " &raquo;; ProcTime: ".ReadingsVal("$name","sql_processing_time", undef)." sec"} <br>
  3776. attr Rep.Agent timeout 3600 <br>
  3777. </code>
  3778. <br>
  3779. </ul>
  3780. </ul>
  3781. =end html_DE
  3782. =cut