Compare commits
11 Commits
report_ind
...
main
| Author | SHA1 | Date |
|---|---|---|
|
|
f5dfda57b7 | |
|
|
e6c47bac4e | |
|
|
d1e6cc19f6 | |
|
|
bb6ebbc9db | |
|
|
fa6eda5c16 | |
|
|
563be46da6 | |
|
|
b10d5f623e | |
|
|
3207c3f51f | |
|
|
2a6fc39dc0 | |
|
|
a200ff729a | |
|
|
1d8fbd49d1 |
Binary file not shown.
|
|
@ -1,28 +0,0 @@
|
|||
\relax
|
||||
\providecommand\hyper@newdestlabel[2]{}
|
||||
\providecommand\HyField@AuxAddToFields[1]{}
|
||||
\providecommand\HyField@AuxAddToCoFields[2]{}
|
||||
\abx@aux@refcontext{nty/global//global/global}
|
||||
\providecommand \oddpage@label [2]{}
|
||||
\@writefile{toc}{~\hfill \textbf {Page}\par }
|
||||
\@writefile{toc}{\contentsline {section}{\numberline {1}Test Case}{2}{section.1}\protected@file@percent }
|
||||
\newlabel{sec:test_case}{{1}{2}{Test Case}{section.1}{}}
|
||||
\newlabel{sec:test_case@cref}{{[section][1][]1}{[1][2][]2}}
|
||||
\@writefile{toc}{\contentsline {section}{\numberline {2}Test Specification}{2}{section.2}\protected@file@percent }
|
||||
\newlabel{sec:test_spec}{{2}{2}{Test Specification}{section.2}{}}
|
||||
\newlabel{sec:test_spec@cref}{{[section][2][]2}{[1][2][]2}}
|
||||
\@writefile{toc}{\contentsline {section}{\numberline {3}Test Results}{2}{section.3}\protected@file@percent }
|
||||
\newlabel{sec:test_results}{{3}{2}{Test Results}{section.3}{}}
|
||||
\newlabel{sec:test_results@cref}{{[section][3][]3}{[1][2][]2}}
|
||||
\@writefile{lof}{\contentsline {figure}{\numberline {1}{\ignorespaces Results from the dropout test}}{2}{figure.caption.1}\protected@file@percent }
|
||||
\providecommand*\caption@xref[2]{\@setref\relax\@undefined{#1}}
|
||||
\newlabel{fig:results}{{1}{2}{Results from the dropout test}{figure.caption.1}{}}
|
||||
\newlabel{fig:results@cref}{{[figure][1][]1}{[1][2][]2}}
|
||||
\@writefile{toc}{\contentsline {section}{\numberline {4}Discussion and Outlook}{3}{section.4}\protected@file@percent }
|
||||
\newlabel{sec:discussion}{{4}{3}{Discussion and Outlook}{section.4}{}}
|
||||
\newlabel{sec:discussion@cref}{{[section][4][]4}{[1][3][]3}}
|
||||
\newlabel{LastPage}{{4}{3}{Discussion and Outlook}{page.3}{}}
|
||||
\gdef\lastpage@lastpage{3}
|
||||
\gdef\lastpage@lastpageHy{3}
|
||||
\abx@aux@read@bbl@mdfivesum{D41D8CD98F00B204E9800998ECF8427E}
|
||||
\gdef \@abspage@last{3}
|
||||
2394
build/main.bcf
2394
build/main.bcf
File diff suppressed because it is too large
Load Diff
|
|
@ -1,4 +0,0 @@
|
|||
[0] Config.pm:307> INFO - This is Biber 2.19
|
||||
[0] Config.pm:310> INFO - Logfile is 'build/main.blg'
|
||||
[108] biber:340> INFO - === Mon Jun 24, 2024, 10:26:41
|
||||
[129] Biber.pm:419> INFO - Reading 'build/main.bcf'
|
||||
|
|
@ -1,251 +0,0 @@
|
|||
# Fdb version 4
|
||||
["biber build/main"] 1719217600.0857 "build/main.bcf" "build/main.bbl" "build/main" 1719438278.8597 0
|
||||
"build/main.bcf" 1719438278.66678 107256 bbb7563979c1f9afdf84ee4ba713182f "pdflatex"
|
||||
(generated)
|
||||
"build/main.bbl"
|
||||
"build/main.blg"
|
||||
(rewritten before read)
|
||||
["pdflatex"] 1719438275.13617 "main.tex" "build/main.pdf" "main" 1719438278.86009 0
|
||||
"/usr/share/texmf-dist/fonts/enc/dvips/cm-super/cm-super-t1.enc" 1716849630 2971 def0b6c1f0b107b3b936def894055589 ""
|
||||
"/usr/share/texmf-dist/fonts/tfm/jknappen/ec/ecbi0800.tfm" 1716849630 3072 356816d08702a080511d9b4663f7e425 ""
|
||||
"/usr/share/texmf-dist/fonts/tfm/jknappen/ec/ecbi1000.tfm" 1716849630 3072 e6fe53b666f9cbd66cb135c6fdea66d3 ""
|
||||
"/usr/share/texmf-dist/fonts/tfm/jknappen/ec/ecbx1000.tfm" 1716849630 3584 2d666ecf6d466d8b007246bc2f94d9da ""
|
||||
"/usr/share/texmf-dist/fonts/tfm/jknappen/ec/eccc1440.tfm" 1716849630 3072 f2bcba5ec52607af3501d45108193ff5 ""
|
||||
"/usr/share/texmf-dist/fonts/tfm/jknappen/ec/eccc2488.tfm" 1716849630 3072 0a9bb7969372717e6b61ec14d5eee9b0 ""
|
||||
"/usr/share/texmf-dist/fonts/tfm/jknappen/ec/ecrm1000.tfm" 1716849630 3584 adb004a0c8e7c46ee66cad73671f37b4 ""
|
||||
"/usr/share/texmf-dist/fonts/tfm/jknappen/ec/ecrm1200.tfm" 1716849630 3584 f80ddd985bd00e29e9a6047ebd9d4781 ""
|
||||
"/usr/share/texmf-dist/fonts/tfm/jknappen/ec/ecrm1440.tfm" 1716849630 3584 3169d30142b88a27d4ab0e3468e963a2 ""
|
||||
"/usr/share/texmf-dist/fonts/tfm/jknappen/ec/ecrm1728.tfm" 1716849630 3584 3c76ccb63eda935a68ba65ba9da29f1a ""
|
||||
"/usr/share/texmf-dist/fonts/tfm/jknappen/ec/ecrm2488.tfm" 1716849630 3584 406ad7b70d9a41f7833f92b6313150c8 ""
|
||||
"/usr/share/texmf-dist/fonts/tfm/jknappen/ec/ecti0800.tfm" 1716849630 3072 828ba0ea87cf5b727c4bfd6367195ec2 ""
|
||||
"/usr/share/texmf-dist/fonts/tfm/jknappen/ec/ecti1000.tfm" 1716849630 3072 3bce340d4c075dffe6d4ec732b4c32fe ""
|
||||
"/usr/share/texmf-dist/fonts/tfm/jknappen/ec/ectt1000.tfm" 1716849630 1536 06717a2b50de47d4087ac0e6cd759455 ""
|
||||
"/usr/share/texmf-dist/fonts/tfm/public/amsfonts/cmextra/cmex7.tfm" 1716849630 1004 54797486969f23fa377b128694d548df ""
|
||||
"/usr/share/texmf-dist/fonts/tfm/public/amsfonts/cmextra/cmex8.tfm" 1716849630 988 bdf658c3bfc2d96d3c8b02cfc1c94c20 ""
|
||||
"/usr/share/texmf-dist/fonts/tfm/public/amsfonts/symbols/msam10.tfm" 1716849630 916 f87d7c45f9c908e672703b83b72241a3 ""
|
||||
"/usr/share/texmf-dist/fonts/tfm/public/amsfonts/symbols/msam5.tfm" 1716849630 924 9904cf1d39e9767e7a3622f2a125a565 ""
|
||||
"/usr/share/texmf-dist/fonts/tfm/public/amsfonts/symbols/msam7.tfm" 1716849630 928 2dc8d444221b7a635bb58038579b861a ""
|
||||
"/usr/share/texmf-dist/fonts/tfm/public/amsfonts/symbols/msbm10.tfm" 1716849630 908 2921f8a10601f252058503cc6570e581 ""
|
||||
"/usr/share/texmf-dist/fonts/tfm/public/amsfonts/symbols/msbm5.tfm" 1716849630 940 75ac932a52f80982a9f8ea75d03a34cf ""
|
||||
"/usr/share/texmf-dist/fonts/tfm/public/amsfonts/symbols/msbm7.tfm" 1716849630 940 228d6584342e91276bf566bcf9716b83 ""
|
||||
"/usr/share/texmf-dist/fonts/tfm/public/cm/cmex10.tfm" 1716849630 992 662f679a0b3d2d53c1b94050fdaa3f50 ""
|
||||
"/usr/share/texmf-dist/fonts/tfm/public/cm/cmmi12.tfm" 1716849630 1524 4414a8315f39513458b80dfc63bff03a ""
|
||||
"/usr/share/texmf-dist/fonts/tfm/public/cm/cmmi6.tfm" 1716849630 1512 f21f83efb36853c0b70002322c1ab3ad ""
|
||||
"/usr/share/texmf-dist/fonts/tfm/public/cm/cmmi8.tfm" 1716849630 1520 eccf95517727cb11801f4f1aee3a21b4 ""
|
||||
"/usr/share/texmf-dist/fonts/tfm/public/cm/cmr12.tfm" 1716849630 1288 655e228510b4c2a1abe905c368440826 ""
|
||||
"/usr/share/texmf-dist/fonts/tfm/public/cm/cmr6.tfm" 1716849630 1300 b62933e007d01cfd073f79b963c01526 ""
|
||||
"/usr/share/texmf-dist/fonts/tfm/public/cm/cmr8.tfm" 1716849630 1292 21c1c5bfeaebccffdb478fd231a0997d ""
|
||||
"/usr/share/texmf-dist/fonts/tfm/public/cm/cmsy10.tfm" 1716849630 1124 6c73e740cf17375f03eec0ee63599741 ""
|
||||
"/usr/share/texmf-dist/fonts/tfm/public/cm/cmsy6.tfm" 1716849630 1116 933a60c408fc0a863a92debe84b2d294 ""
|
||||
"/usr/share/texmf-dist/fonts/tfm/public/cm/cmsy8.tfm" 1716849630 1120 8b7d695260f3cff42e636090a8002094 ""
|
||||
"/usr/share/texmf-dist/fonts/type1/public/amsfonts/cm/cmmi10.pfb" 1716849630 36299 5f9df58c2139e7edcf37c8fca4bd384d ""
|
||||
"/usr/share/texmf-dist/fonts/type1/public/cm-super/sfbi0800.pfb" 1716849630 199864 78641615cf96b4cff2c8a97c4dcf53a2 ""
|
||||
"/usr/share/texmf-dist/fonts/type1/public/cm-super/sfcc1440.pfb" 1716849630 110032 35e0196b03e20c9bc327f34ecd2026f9 ""
|
||||
"/usr/share/texmf-dist/fonts/type1/public/cm-super/sfcc2488.pfb" 1716849630 107888 79f3c4bd5eeba2adb9fc0d3049dd8ef0 ""
|
||||
"/usr/share/texmf-dist/fonts/type1/public/cm-super/sfrm1000.pfb" 1716849630 138258 6525c253f16cededa14c7fd0da7f67b2 ""
|
||||
"/usr/share/texmf-dist/fonts/type1/public/cm-super/sfrm1200.pfb" 1716849630 136101 f533469f523533d38317ab5729d00c8a ""
|
||||
"/usr/share/texmf-dist/fonts/type1/public/cm-super/sfti0800.pfb" 1716849630 187625 f02a8c2c788e6490af6fc4f5ed857a0c ""
|
||||
"/usr/share/texmf-dist/fonts/type1/public/cm-super/sftt1000.pfb" 1716849630 169201 9ebf99020dde51a5086e186761a34e8f ""
|
||||
"/usr/share/texmf-dist/tex/context/base/mkii/supp-pdf.mkii" 1716849630 71627 94eb9990bed73c364d7f53f960cc8c5b ""
|
||||
"/usr/share/texmf-dist/tex/generic/atbegshi/atbegshi.sty" 1716849630 24708 5584a51a7101caf7e6bbf1fc27d8f7b1 ""
|
||||
"/usr/share/texmf-dist/tex/generic/bigintcalc/bigintcalc.sty" 1716849630 40635 c40361e206be584d448876bba8a64a3b ""
|
||||
"/usr/share/texmf-dist/tex/generic/bitset/bitset.sty" 1716849630 33961 6b5c75130e435b2bfdb9f480a09a39f9 ""
|
||||
"/usr/share/texmf-dist/tex/generic/gettitlestring/gettitlestring.sty" 1716849630 8371 9d55b8bd010bc717624922fb3477d92e ""
|
||||
"/usr/share/texmf-dist/tex/generic/iftex/iftex.sty" 1716849630 7237 bdd120a32c8fdb4b433cf9ca2e7cd98a ""
|
||||
"/usr/share/texmf-dist/tex/generic/iftex/ifvtex.sty" 1716849630 1057 525c2192b5febbd8c1f662c9468335bb ""
|
||||
"/usr/share/texmf-dist/tex/generic/infwarerr/infwarerr.sty" 1716849630 8356 7bbb2c2373aa810be568c29e333da8ed ""
|
||||
"/usr/share/texmf-dist/tex/generic/intcalc/intcalc.sty" 1716849630 31769 002a487f55041f8e805cfbf6385ffd97 ""
|
||||
"/usr/share/texmf-dist/tex/generic/kvdefinekeys/kvdefinekeys.sty" 1716849630 5412 d5a2436094cd7be85769db90f29250a6 ""
|
||||
"/usr/share/texmf-dist/tex/generic/ltxcmds/ltxcmds.sty" 1716849630 17865 1a9bd36b4f98178fa551aca822290953 ""
|
||||
"/usr/share/texmf-dist/tex/generic/pdfescape/pdfescape.sty" 1716849630 19007 15924f7228aca6c6d184b115f4baa231 ""
|
||||
"/usr/share/texmf-dist/tex/generic/pdftexcmds/pdftexcmds.sty" 1716849630 20089 80423eac55aa175305d35b49e04fe23b ""
|
||||
"/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcore.code.tex" 1716849630 1016 1c2b89187d12a2768764b83b4945667c ""
|
||||
"/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorearrows.code.tex" 1716849630 43820 1fef971b75380574ab35a0d37fd92608 ""
|
||||
"/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreexternal.code.tex" 1716849630 19324 f4e4c6403dd0f1605fd20ed22fa79dea ""
|
||||
"/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoregraphicstate.code.tex" 1716849630 6038 ccb406740cc3f03bbfb58ad504fe8c27 ""
|
||||
"/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreimage.code.tex" 1716849630 6911 f6d4cf5a3fef5cc879d668b810e82868 ""
|
||||
"/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorelayers.code.tex" 1716849630 4883 42daaf41e27c3735286e23e48d2d7af9 ""
|
||||
"/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreobjects.code.tex" 1716849630 2544 8c06d2a7f0f469616ac9e13db6d2f842 ""
|
||||
"/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathconstruct.code.tex" 1716849630 44195 5e390c414de027626ca5e2df888fa68d ""
|
||||
"/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathprocessing.code.tex" 1716849630 17311 2ef6b2e29e2fc6a2fc8d6d652176e257 ""
|
||||
"/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathusage.code.tex" 1716849630 21302 788a79944eb22192a4929e46963a3067 ""
|
||||
"/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepatterns.code.tex" 1716849630 9691 3d42d89522f4650c2f3dc616ca2b925e ""
|
||||
"/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepoints.code.tex" 1716849630 33335 dd1fa4814d4e51f18be97d88bf0da60c ""
|
||||
"/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorequick.code.tex" 1716849630 2965 4c2b1f4e0826925746439038172e5d6f ""
|
||||
"/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorerdf.code.tex" 1716849630 5196 2cc249e0ee7e03da5f5f6589257b1e5b ""
|
||||
"/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorescopes.code.tex" 1716849630 20821 7579108c1e9363e61a0b1584778804aa ""
|
||||
"/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreshade.code.tex" 1716849630 35249 abd4adf948f960299a4b3d27c5dddf46 ""
|
||||
"/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoretransformations.code.tex" 1716849630 22012 81b34a0aa8fa1a6158cc6220b00e4f10 ""
|
||||
"/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoretransparency.code.tex" 1716849630 8893 e851de2175338fdf7c17f3e091d94618 ""
|
||||
"/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarypositioning.code.tex" 1716849630 3937 3f208572dd82c71103831da976d74f1a ""
|
||||
"/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarytopaths.code.tex" 1716849630 11518 738408f795261b70ce8dd47459171309 ""
|
||||
"/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/tikz.code.tex" 1716849630 186782 af500404a9edec4d362912fe762ded92 ""
|
||||
"/usr/share/texmf-dist/tex/generic/pgf/libraries/pgflibraryplothandlers.code.tex" 1716849630 32995 ac577023e12c0e4bd8aa420b2e852d1a ""
|
||||
"/usr/share/texmf-dist/tex/generic/pgf/math/pgfint.code.tex" 1716849630 3063 8c415c68a0f3394e45cfeca0b65f6ee6 ""
|
||||
"/usr/share/texmf-dist/tex/generic/pgf/math/pgfmath.code.tex" 1716849630 949 cea70942e7b7eddabfb3186befada2e6 ""
|
||||
"/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathcalc.code.tex" 1716849630 13270 2e54f2ce7622437bf37e013d399743e3 ""
|
||||
"/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfloat.code.tex" 1716849630 104717 9b2393fbf004a0ce7fa688dbce423848 ""
|
||||
"/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.base.code.tex" 1716849630 10165 cec5fa73d49da442e56efc2d605ef154 ""
|
||||
"/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.basic.code.tex" 1716849630 28178 41c17713108e0795aac6fef3d275fbca ""
|
||||
"/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.code.tex" 1716849630 9649 85779d3d8d573bfd2cd4137ba8202e60 ""
|
||||
"/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.comparison.code.tex" 1716849630 3865 ac538ab80c5cf82b345016e474786549 ""
|
||||
"/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.integerarithmetics.code.tex" 1716849630 3177 27d85c44fbfe09ff3b2cf2879e3ea434 ""
|
||||
"/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.misc.code.tex" 1716849630 11024 0179538121bc2dba172013a3ef89519f ""
|
||||
"/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.random.code.tex" 1716849630 7890 0a86dbf4edfd88d022e0d889ec78cc03 ""
|
||||
"/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.round.code.tex" 1716849630 3379 781797a101f647bab82741a99944a229 ""
|
||||
"/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.trigonometric.code.tex" 1716849630 92405 f515f31275db273f97b9d8f52e1b0736 ""
|
||||
"/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathparser.code.tex" 1716849630 37466 97b0a1ba732e306a1a2034f5a73e239f ""
|
||||
"/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathutil.code.tex" 1716849630 8471 c2883569d03f69e8e1cabfef4999cfd7 ""
|
||||
"/usr/share/texmf-dist/tex/generic/pgf/modules/pgfmodulematrix.code.tex" 1716849630 21211 1e73ec76bd73964d84197cc3d2685b01 ""
|
||||
"/usr/share/texmf-dist/tex/generic/pgf/modules/pgfmoduleplot.code.tex" 1716849630 16121 346f9013d34804439f7436ff6786cef7 ""
|
||||
"/usr/share/texmf-dist/tex/generic/pgf/modules/pgfmoduleshapes.code.tex" 1716849630 44792 271e2e1934f34c759f4dedb1e14a5015 ""
|
||||
"/usr/share/texmf-dist/tex/generic/pgf/pgf.revision.tex" 1716849630 114 e6d443369d0673933b38834bf99e422d ""
|
||||
"/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgf.cfg" 1716849630 926 2963ea0dcf6cc6c0a770b69ec46a477b ""
|
||||
"/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsys-common-pdf.def" 1716849630 5542 32f75a31ea6c3a7e1148cd6d5e93dbb7 ""
|
||||
"/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsys-pdftex.def" 1716849630 12612 7774ba67bfd72e593c4436c2de6201e3 ""
|
||||
"/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsys.code.tex" 1716849630 61351 bc5f86e0355834391e736e97a61abced ""
|
||||
"/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsysprotocol.code.tex" 1716849630 1896 b8e0ca0ac371d74c0ca05583f6313c91 ""
|
||||
"/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsyssoftpath.code.tex" 1716849630 7778 53c8b5623d80238f6a20aa1df1868e63 ""
|
||||
"/usr/share/texmf-dist/tex/generic/pgf/utilities/pgffor.code.tex" 1716849630 24033 d8893a1ec4d1bfa101b172754743d340 ""
|
||||
"/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfkeys.code.tex" 1716849630 39784 414c54e866ebab4b801e2ad81d9b21d8 ""
|
||||
"/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfkeyslibraryfiltered.code.tex" 1716849630 37433 940bc6d409f1ffd298adfdcaf125dd86 ""
|
||||
"/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfrcs.code.tex" 1716849630 4385 510565c2f07998c8a0e14f0ec07ff23c ""
|
||||
"/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfutil-common.tex" 1716849630 29239 22e8c7516012992a49873eff0d868fed ""
|
||||
"/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfutil-latex.def" 1716849630 6950 8524a062d82b7afdc4a88a57cb377784 ""
|
||||
"/usr/share/texmf-dist/tex/generic/stringenc/se-pdfdoc.def" 1716849630 5108 8920602307ea1294ccbce2300c7c6ccb ""
|
||||
"/usr/share/texmf-dist/tex/generic/stringenc/stringenc.sty" 1716849630 21514 b7557edcee22835ef6b03ede1802dad4 ""
|
||||
"/usr/share/texmf-dist/tex/generic/uniquecounter/uniquecounter.sty" 1716849630 7008 f92eaa0a3872ed622bbf538217cd2ab7 ""
|
||||
"/usr/share/texmf-dist/tex/generic/xkeyval/xkeyval.tex" 1716849630 19231 27205ee17aaa2902aea3e0c07a3cfc65 ""
|
||||
"/usr/share/texmf-dist/tex/generic/xkeyval/xkvutils.tex" 1716849630 7677 9cb1a74d945bc9331f2181c0a59ff34a ""
|
||||
"/usr/share/texmf-dist/tex/latex/adjustbox/adjcalc.sty" 1716849630 5598 c49b91713cbe5e50a1fabefb733eda0d ""
|
||||
"/usr/share/texmf-dist/tex/latex/adjustbox/adjustbox.sty" 1716849630 56907 e3e515e490dbc35309a010b5bbe1bef5 ""
|
||||
"/usr/share/texmf-dist/tex/latex/adjustbox/tc-pdftex.def" 1716849630 4070 1677cfee6374067b93f61cf57ecd7144 ""
|
||||
"/usr/share/texmf-dist/tex/latex/adjustbox/trimclip.sty" 1716849630 7244 36558f478da08e083d7316a63ba4bcd6 ""
|
||||
"/usr/share/texmf-dist/tex/latex/amsfonts/amsfonts.sty" 1716849630 5949 3f3fd50a8cc94c3d4cbf4fc66cd3df1c ""
|
||||
"/usr/share/texmf-dist/tex/latex/amsfonts/amssymb.sty" 1716849630 13829 94730e64147574077f8ecfea9bb69af4 ""
|
||||
"/usr/share/texmf-dist/tex/latex/amsfonts/umsa.fd" 1716849630 961 6518c6525a34feb5e8250ffa91731cff ""
|
||||
"/usr/share/texmf-dist/tex/latex/amsfonts/umsb.fd" 1716849630 961 d02606146ba5601b5645f987c92e6193 ""
|
||||
"/usr/share/texmf-dist/tex/latex/amsmath/amsbsy.sty" 1716849630 2222 499d61426192c39efd8f410ee1a52b9c ""
|
||||
"/usr/share/texmf-dist/tex/latex/amsmath/amsgen.sty" 1716849630 4173 82ac04dfb1256038fad068287fbb4fe6 ""
|
||||
"/usr/share/texmf-dist/tex/latex/amsmath/amsmath.sty" 1716849630 88371 d84032c0f422c3d1e282266c01bef237 ""
|
||||
"/usr/share/texmf-dist/tex/latex/amsmath/amsopn.sty" 1716849630 4474 b811654f4bf125f11506d13d13647efb ""
|
||||
"/usr/share/texmf-dist/tex/latex/amsmath/amstext.sty" 1716849630 2444 0d0c1ee65478277e8015d65b86983da2 ""
|
||||
"/usr/share/texmf-dist/tex/latex/appendix/appendix.sty" 1716849630 8878 d9f65b39ca82f1d70030390eca653b1c ""
|
||||
"/usr/share/texmf-dist/tex/latex/atveryend/atveryend.sty" 1716849630 19336 ce7ae9438967282886b3b036cfad1e4d ""
|
||||
"/usr/share/texmf-dist/tex/latex/auxhook/auxhook.sty" 1716849630 3935 57aa3c3e203a5c2effb4d2bd2efbc323 ""
|
||||
"/usr/share/texmf-dist/tex/latex/base/article.cls" 1716849630 20144 147463a6a579f4597269ef9565205cfe ""
|
||||
"/usr/share/texmf-dist/tex/latex/base/atbegshi-ltx.sty" 1716849630 3045 273c666a54e60b9f730964f431a56c1b ""
|
||||
"/usr/share/texmf-dist/tex/latex/base/atveryend-ltx.sty" 1716849630 2462 6bc53756156dbd71c1ad550d30a3b93f ""
|
||||
"/usr/share/texmf-dist/tex/latex/base/fontenc.sty" 1716849630 5119 a04a8b68ab4f6ce800a41f7f8012a10e ""
|
||||
"/usr/share/texmf-dist/tex/latex/base/ifthen.sty" 1716849630 5319 2b738d02ce36ada6dcdd9534940db0ee ""
|
||||
"/usr/share/texmf-dist/tex/latex/base/inputenc.sty" 1716849630 5048 425739d70251273bf93e3d51f3c40048 ""
|
||||
"/usr/share/texmf-dist/tex/latex/base/size10.clo" 1716849630 8448 dbc0dbf4156c0bb9ba01a1c685d3bad0 ""
|
||||
"/usr/share/texmf-dist/tex/latex/base/t1cmtt.fd" 1716849630 2443 790016d75def8d3127df5c216a45abcc ""
|
||||
"/usr/share/texmf-dist/tex/latex/biblatex/bbx/numeric.bbx" 1716849630 1818 9ed166ac0a9204a8ebe450ca09db5dde ""
|
||||
"/usr/share/texmf-dist/tex/latex/biblatex/bbx/standard.bbx" 1716849630 25680 409c3f3d570418bc545e8065bebd0688 ""
|
||||
"/usr/share/texmf-dist/tex/latex/biblatex/biblatex.cfg" 1716849630 69 249fa6df04d948e51b6d5c67bea30c42 ""
|
||||
"/usr/share/texmf-dist/tex/latex/biblatex/biblatex.def" 1716849630 92527 8f6b3a677f74ea525477a813f33c4e65 ""
|
||||
"/usr/share/texmf-dist/tex/latex/biblatex/biblatex.sty" 1716849630 528517 7eed285c714f532e12ae48b360c080f8 ""
|
||||
"/usr/share/texmf-dist/tex/latex/biblatex/blx-case-expl3.sty" 1716849630 8433 72f8188742e7214b7068f345cd0287ac ""
|
||||
"/usr/share/texmf-dist/tex/latex/biblatex/blx-compat.def" 1716849630 13919 5426dbe90e723f089052b4e908b56ef9 ""
|
||||
"/usr/share/texmf-dist/tex/latex/biblatex/blx-dm.def" 1716849630 32455 8d3e554836db11aab80a8e11be62e1b1 ""
|
||||
"/usr/share/texmf-dist/tex/latex/biblatex/cbx/numeric.cbx" 1716849630 4629 cda468e8a0b1cfa0f61872e171037a4b ""
|
||||
"/usr/share/texmf-dist/tex/latex/biblatex/lbx/english.lbx" 1716849630 39965 48ce9ce3350aba9457f1020b1deba5cf ""
|
||||
"/usr/share/texmf-dist/tex/latex/caption/caption.sty" 1716849630 56128 c2ccf1a29d78c33bc553880402e4fb9a ""
|
||||
"/usr/share/texmf-dist/tex/latex/caption/caption3.sty" 1716849630 72619 ee90b6612147680fd73c3b1406a74245 ""
|
||||
"/usr/share/texmf-dist/tex/latex/caption/subcaption.sty" 1716849630 12494 0c0cdb824278a4d51cefeb2e79901315 ""
|
||||
"/usr/share/texmf-dist/tex/latex/cleveref/cleveref.sty" 1716849630 329481 7fc6b003158402a4c694bc0a1b729308 ""
|
||||
"/usr/share/texmf-dist/tex/latex/collectbox/collectbox.sty" 1716849630 9124 59c3b56f1a073de66e3eea35f9c173c8 ""
|
||||
"/usr/share/texmf-dist/tex/latex/csquotes/csquotes.cfg" 1716849630 7068 06f8d141725d114847527a66439066b6 ""
|
||||
"/usr/share/texmf-dist/tex/latex/csquotes/csquotes.def" 1716849630 21994 2bc2a882df92d97dbce35c06bb1d3541 ""
|
||||
"/usr/share/texmf-dist/tex/latex/csquotes/csquotes.sty" 1716849630 62761 3c92d495a9e03d7254b9a3266aa17164 ""
|
||||
"/usr/share/texmf-dist/tex/latex/epstopdf-pkg/epstopdf-base.sty" 1716849630 13886 d1306dcf79a944f6988e688c1785f9ce ""
|
||||
"/usr/share/texmf-dist/tex/latex/eso-pic/eso-pic.sty" 1716849630 11876 6ef493863ae0d7a984706973240c2237 ""
|
||||
"/usr/share/texmf-dist/tex/latex/etoolbox/etoolbox.sty" 1716849630 46845 3b58f70c6e861a13d927bff09d35ecbc ""
|
||||
"/usr/share/texmf-dist/tex/latex/fancyhdr/fancyhdr.sty" 1716849630 18450 88279bf67c81e69f8e3f1c1bad1a26c5 ""
|
||||
"/usr/share/texmf-dist/tex/latex/float/float.sty" 1716849630 6749 16d2656a1984957e674b149555f1ea1d ""
|
||||
"/usr/share/texmf-dist/tex/latex/footmisc/footmisc.sty" 1716849630 21399 e9fa1517a82f349507e998594ef20b82 ""
|
||||
"/usr/share/texmf-dist/tex/latex/geometry/geometry.sty" 1716849630 41601 9cf6c5257b1bc7af01a58859749dd37a ""
|
||||
"/usr/share/texmf-dist/tex/latex/graphics-cfg/color.cfg" 1716849630 1213 620bba36b25224fa9b7e1ccb4ecb76fd ""
|
||||
"/usr/share/texmf-dist/tex/latex/graphics-cfg/graphics.cfg" 1716849630 1224 978390e9c2234eab29404bc21b268d1e ""
|
||||
"/usr/share/texmf-dist/tex/latex/graphics-def/pdftex.def" 1716849630 19448 1e988b341dda20961a6b931bcde55519 ""
|
||||
"/usr/share/texmf-dist/tex/latex/graphics/color.sty" 1716849630 7233 e46ce9241d2b2ca2a78155475fdd557a ""
|
||||
"/usr/share/texmf-dist/tex/latex/graphics/graphics.sty" 1716849630 18387 8f900a490197ebaf93c02ae9476d4b09 ""
|
||||
"/usr/share/texmf-dist/tex/latex/graphics/graphicx.sty" 1716849630 8010 a8d949cbdbc5c983593827c9eec252e1 ""
|
||||
"/usr/share/texmf-dist/tex/latex/graphics/keyval.sty" 1716849630 2671 7e67d78d9b88c845599a85b2d41f2e39 ""
|
||||
"/usr/share/texmf-dist/tex/latex/graphics/lscape.sty" 1716849630 1822 5e4f855a9ecb640f34881e4b457fa9aa ""
|
||||
"/usr/share/texmf-dist/tex/latex/graphics/mathcolor.ltx" 1716849630 2885 9c645d672ae17285bba324998918efd8 ""
|
||||
"/usr/share/texmf-dist/tex/latex/graphics/trig.sty" 1716849630 4023 293ea1c16429fc0c4cf605f4da1791a9 ""
|
||||
"/usr/share/texmf-dist/tex/latex/hycolor/hycolor.sty" 1716849630 17914 4c28a13fc3d975e6e81c9bea1d697276 ""
|
||||
"/usr/share/texmf-dist/tex/latex/hyperref/hpdftex.def" 1716849630 48154 e46bf8adeb936500541441171d61726d ""
|
||||
"/usr/share/texmf-dist/tex/latex/hyperref/hyperref.sty" 1716849630 220920 fd3cbb5f1a2bc9b8f451b8b7d8171264 ""
|
||||
"/usr/share/texmf-dist/tex/latex/hyperref/nameref.sty" 1716849630 11026 182c63f139a71afd30a28e5f1ed2cd1c ""
|
||||
"/usr/share/texmf-dist/tex/latex/hyperref/pd1enc.def" 1716849630 14249 e67cb186717b7ab18d14a4875e7e98b5 ""
|
||||
"/usr/share/texmf-dist/tex/latex/hyperref/psdextra.def" 1716849630 39335 60d0b1a7945c7036be4d77ccc6e266f3 ""
|
||||
"/usr/share/texmf-dist/tex/latex/hyperref/puenc.def" 1716849630 117112 05831178ece2cad4d9629dcf65099b11 ""
|
||||
"/usr/share/texmf-dist/tex/latex/ifoddpage/ifoddpage.sty" 1716849630 2142 eae42205b97b7a3ad0e58db5fe99e3e6 ""
|
||||
"/usr/share/texmf-dist/tex/latex/kvoptions/kvoptions.sty" 1716849630 22555 6d8e155cfef6d82c3d5c742fea7c992e ""
|
||||
"/usr/share/texmf-dist/tex/latex/kvsetkeys/kvsetkeys.sty" 1716849630 13815 760b0c02f691ea230f5359c4e1de23a7 ""
|
||||
"/usr/share/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def" 1716849630 30006 3d512c0edd558928ddea1690180ef77e ""
|
||||
"/usr/share/texmf-dist/tex/latex/l3kernel/expl3.sty" 1716849630 6565 10e89ed128ccd59431746bbdd82129fc ""
|
||||
"/usr/share/texmf-dist/tex/latex/l3packages/l3keys2e/l3keys2e.sty" 1716849630 4674 4240ebde863d19dc4d13bb280b78abb2 ""
|
||||
"/usr/share/texmf-dist/tex/latex/l3packages/xparse/xparse.sty" 1716849630 9327 11bedad2ac38f92e405a38ed18489a03 ""
|
||||
"/usr/share/texmf-dist/tex/latex/lastpage/lastpage.sty" 1716849630 2868 94ccb360e6e4443cad221f78a5cbb6a8 ""
|
||||
"/usr/share/texmf-dist/tex/latex/lastpage/lastpage2e.sty" 1716849630 2720 a038de4136aa4a721c8ef0dc226ae6b5 ""
|
||||
"/usr/share/texmf-dist/tex/latex/lastpage/lastpagemodern.sty" 1716849630 9166 74f0d39535736e850fd2f2796fd37674 ""
|
||||
"/usr/share/texmf-dist/tex/latex/latexconfig/epstopdf-sys.cfg" 1716849630 678 4792914a8f45be57bb98413425e4c7af ""
|
||||
"/usr/share/texmf-dist/tex/latex/lipsum/lipsum.ltd.tex" 1716849630 95525 6fd0552101a6b1f9b7a84b402ec435ba ""
|
||||
"/usr/share/texmf-dist/tex/latex/lipsum/lipsum.sty" 1716849630 14690 c2c754218a7108db7823a4839c1bc3cd ""
|
||||
"/usr/share/texmf-dist/tex/latex/listings/listings.cfg" 1716849630 1830 20af84c556326f7c12b9202ebe363f56 ""
|
||||
"/usr/share/texmf-dist/tex/latex/listings/listings.sty" 1716849630 81322 d02238bdeb305f2c9f9d0229f99371d0 ""
|
||||
"/usr/share/texmf-dist/tex/latex/listings/lstmisc.sty" 1716849630 77022 5c8c440739265e7ba15b8379ece6ecd7 ""
|
||||
"/usr/share/texmf-dist/tex/latex/listings/lstpatch.sty" 1716849630 329 f19f5da7234b51d16764e23d20999c73 ""
|
||||
"/usr/share/texmf-dist/tex/latex/logreq/logreq.def" 1716849630 1620 fb1c32b818f2058eca187e5c41dfae77 ""
|
||||
"/usr/share/texmf-dist/tex/latex/logreq/logreq.sty" 1716849630 6187 b27afc771af565d3a9ff1ca7d16d0d46 ""
|
||||
"/usr/share/texmf-dist/tex/latex/mathtools/mathtools.sty" 1716849630 62672 9ff036bc89365461cc2bd482cc1e4879 ""
|
||||
"/usr/share/texmf-dist/tex/latex/mathtools/mhsetup.sty" 1716849630 5582 a43dedf8e5ec418356f1e9dfe5d29fc3 ""
|
||||
"/usr/share/texmf-dist/tex/latex/outlines/outlines.sty" 1716849630 4614 0e87f5d4bbe7b8f03b29c7941684b222 ""
|
||||
"/usr/share/texmf-dist/tex/latex/pdflscape/pdflscape-nometadata.sty" 1716849630 6572 ea530fbbe537629fd97736d33babc07d ""
|
||||
"/usr/share/texmf-dist/tex/latex/pdflscape/pdflscape.sty" 1716849630 2224 1230ab76aa62221ccbd90bca8c8c015e ""
|
||||
"/usr/share/texmf-dist/tex/latex/pdfpages/pdfpages.sty" 1716849630 54914 ea9713532d0d0ae802ca2446650f9ded ""
|
||||
"/usr/share/texmf-dist/tex/latex/pdfpages/pppdftex.def" 1716849630 6591 249ecc067cc3246c4ed39a577ded77e3 ""
|
||||
"/usr/share/texmf-dist/tex/latex/pgf/basiclayer/pgf.sty" 1716849630 1090 bae35ef70b3168089ef166db3e66f5b2 ""
|
||||
"/usr/share/texmf-dist/tex/latex/pgf/basiclayer/pgfcore.sty" 1716849630 373 00b204b1d7d095b892ad31a7494b0373 ""
|
||||
"/usr/share/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version-0-65.sty" 1716849630 21013 f4ff83d25bb56552493b030f27c075ae ""
|
||||
"/usr/share/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version-1-18.sty" 1716849630 989 c49c8ae06d96f8b15869da7428047b1e ""
|
||||
"/usr/share/texmf-dist/tex/latex/pgf/frontendlayer/tikz.sty" 1716849630 339 c2e180022e3afdb99c7d0ea5ce469b7d ""
|
||||
"/usr/share/texmf-dist/tex/latex/pgf/math/pgfmath.sty" 1716849630 306 c56a323ca5bf9242f54474ced10fca71 ""
|
||||
"/usr/share/texmf-dist/tex/latex/pgf/systemlayer/pgfsys.sty" 1716849630 443 8c872229db56122037e86bcda49e14f3 ""
|
||||
"/usr/share/texmf-dist/tex/latex/pgf/utilities/pgffor.sty" 1716849630 348 ee405e64380c11319f0e249fed57e6c5 ""
|
||||
"/usr/share/texmf-dist/tex/latex/pgf/utilities/pgfkeys.sty" 1716849630 274 5ae372b7df79135d240456a1c6f2cf9a ""
|
||||
"/usr/share/texmf-dist/tex/latex/pgf/utilities/pgfrcs.sty" 1716849630 325 f9f16d12354225b7dd52a3321f085955 ""
|
||||
"/usr/share/texmf-dist/tex/latex/refcount/refcount.sty" 1716849630 9878 9e94e8fa600d95f9c7731bb21dfb67a4 ""
|
||||
"/usr/share/texmf-dist/tex/latex/rerunfilecheck/rerunfilecheck.sty" 1716849630 9714 ba3194bd52c8499b3f1e3eb91d409670 ""
|
||||
"/usr/share/texmf-dist/tex/latex/silence/silence.sty" 1716849630 17540 5aad5dddd881dee43ad1a83adb336af9 ""
|
||||
"/usr/share/texmf-dist/tex/latex/titlesec/titlesec.sty" 1716849630 48766 0b93839be28e9744a24c45075c75b2e2 ""
|
||||
"/usr/share/texmf-dist/tex/latex/tocloft/tocloft.sty" 1716849630 36103 3e78d14f0f4b1a30560fea5e04de805d ""
|
||||
"/usr/share/texmf-dist/tex/latex/todonotes/todonotes.sty" 1716849630 21404 916e19cbd009b6d289c8194b313d3895 ""
|
||||
"/usr/share/texmf-dist/tex/latex/tools/calc.sty" 1716849630 10214 547fd4d29642cb7c80bf54b49d447f01 ""
|
||||
"/usr/share/texmf-dist/tex/latex/url/url.sty" 1716849630 12796 8edb7d69a20b857904dd0ea757c14ec9 ""
|
||||
"/usr/share/texmf-dist/tex/latex/varwidth/varwidth.sty" 1716849630 10894 d359a13923460b2a73d4312d613554c8 ""
|
||||
"/usr/share/texmf-dist/tex/latex/xcolor/xcolor.sty" 1716849630 55487 80a65caedd3722f4c20a14a69e785d8f ""
|
||||
"/usr/share/texmf-dist/tex/latex/xkeyval/xkeyval.sty" 1716849630 4937 4ce600ce9bd4ec84d0250eb6892fcf4f ""
|
||||
"/usr/share/texmf-dist/web2c/texmf.cnf" 1716849630 41588 b43d3e860a4f94167ee1e725ff526a72 ""
|
||||
"/var/lib/texmf/fonts/map/pdftex/updmap/pdftex.map" 1716921075.84382 5307366 7c8b2c5f25a30f46e808245b8e6cf269 ""
|
||||
"/var/lib/texmf/web2c/pdftex/pdflatex.fmt" 1716921071 7112998 4ddc129a6d2bdf041fc05b3009008598 ""
|
||||
"build/main.aux" 1719438278.66011 1774 837a1b39d05a8f7362965527e9cc902c "pdflatex"
|
||||
"build/main.bbl" 1719432997.89041 0 d41d8cd98f00b204e9800998ecf8427e "biber build/main"
|
||||
"build/main.out" 1719438278.66344 216 f02a5084175509c0e4a18d378d1398db "pdflatex"
|
||||
"build/main.run.xml" 1719438278.67011 2224 bc6149f5caee4074188e1b81a66978d0 "pdflatex"
|
||||
"img/DTU.pdf" 1719432997.96042 17695 ac89e3011a696efc58efb000e9d735c0 ""
|
||||
"img/dropout.png" 1719437541.95942 597350 5db1ea9199b8e4019172fe7e8582ce8f ""
|
||||
"main.tex" 1719438273.59686 6534 c6d0c4ffe6e83fc8936ddc67cc6c4fd0 ""
|
||||
"preamble.tex" 1719432997.96042 3532 2629b8bd7c0c3e4ffd675fe12836beac ""
|
||||
(generated)
|
||||
"build/main.aux"
|
||||
"build/main.bcf"
|
||||
"build/main.log"
|
||||
"build/main.out"
|
||||
"build/main.pdf"
|
||||
"build/main.run.xml"
|
||||
(rewritten before read)
|
||||
463
build/main.fls
463
build/main.fls
|
|
@ -1,463 +0,0 @@
|
|||
PWD /home/daniel/Dropbox/DTU/F24/46045/syslab
|
||||
INPUT /usr/share/texmf-dist/web2c/texmf.cnf
|
||||
INPUT /var/lib/texmf/web2c/pdftex/pdflatex.fmt
|
||||
INPUT main.tex
|
||||
OUTPUT build/main.log
|
||||
INPUT ./preamble.tex
|
||||
INPUT ./preamble.tex
|
||||
INPUT preamble.tex
|
||||
INPUT /usr/share/texmf-dist/tex/latex/base/article.cls
|
||||
INPUT /usr/share/texmf-dist/tex/latex/base/article.cls
|
||||
INPUT /usr/share/texmf-dist/tex/latex/base/size10.clo
|
||||
INPUT /usr/share/texmf-dist/tex/latex/base/size10.clo
|
||||
INPUT /usr/share/texmf-dist/tex/latex/base/size10.clo
|
||||
INPUT /usr/share/texmf-dist/tex/latex/geometry/geometry.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/geometry/geometry.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/graphics/keyval.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/graphics/keyval.sty
|
||||
INPUT /usr/share/texmf-dist/tex/generic/iftex/ifvtex.sty
|
||||
INPUT /usr/share/texmf-dist/tex/generic/iftex/ifvtex.sty
|
||||
INPUT /usr/share/texmf-dist/tex/generic/iftex/iftex.sty
|
||||
INPUT /usr/share/texmf-dist/tex/generic/iftex/iftex.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/base/fontenc.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/base/fontenc.sty
|
||||
INPUT /usr/share/texmf-dist/fonts/tfm/jknappen/ec/ecrm1000.tfm
|
||||
INPUT /usr/share/texmf-dist/tex/latex/base/inputenc.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/base/inputenc.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/hyperref/hyperref.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/hyperref/hyperref.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/kvsetkeys/kvsetkeys.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/kvsetkeys/kvsetkeys.sty
|
||||
INPUT /usr/share/texmf-dist/tex/generic/kvdefinekeys/kvdefinekeys.sty
|
||||
INPUT /usr/share/texmf-dist/tex/generic/kvdefinekeys/kvdefinekeys.sty
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pdfescape/pdfescape.sty
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pdfescape/pdfescape.sty
|
||||
INPUT /usr/share/texmf-dist/tex/generic/ltxcmds/ltxcmds.sty
|
||||
INPUT /usr/share/texmf-dist/tex/generic/ltxcmds/ltxcmds.sty
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pdftexcmds/pdftexcmds.sty
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pdftexcmds/pdftexcmds.sty
|
||||
INPUT /usr/share/texmf-dist/tex/generic/infwarerr/infwarerr.sty
|
||||
INPUT /usr/share/texmf-dist/tex/generic/infwarerr/infwarerr.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/hycolor/hycolor.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/hycolor/hycolor.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/auxhook/auxhook.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/auxhook/auxhook.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/hyperref/nameref.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/hyperref/nameref.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/refcount/refcount.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/refcount/refcount.sty
|
||||
INPUT /usr/share/texmf-dist/tex/generic/gettitlestring/gettitlestring.sty
|
||||
INPUT /usr/share/texmf-dist/tex/generic/gettitlestring/gettitlestring.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/kvoptions/kvoptions.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/kvoptions/kvoptions.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/etoolbox/etoolbox.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/etoolbox/etoolbox.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/hyperref/pd1enc.def
|
||||
INPUT /usr/share/texmf-dist/tex/latex/hyperref/pd1enc.def
|
||||
INPUT /usr/share/texmf-dist/tex/latex/hyperref/pd1enc.def
|
||||
INPUT /usr/share/texmf-dist/tex/generic/intcalc/intcalc.sty
|
||||
INPUT /usr/share/texmf-dist/tex/generic/intcalc/intcalc.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/hyperref/puenc.def
|
||||
INPUT /usr/share/texmf-dist/tex/latex/hyperref/puenc.def
|
||||
INPUT /usr/share/texmf-dist/tex/latex/hyperref/puenc.def
|
||||
INPUT /usr/share/texmf-dist/tex/generic/stringenc/stringenc.sty
|
||||
INPUT /usr/share/texmf-dist/tex/generic/stringenc/stringenc.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/hyperref/psdextra.def
|
||||
INPUT /usr/share/texmf-dist/tex/latex/hyperref/psdextra.def
|
||||
INPUT /usr/share/texmf-dist/tex/latex/hyperref/psdextra.def
|
||||
INPUT /usr/share/texmf-dist/tex/latex/url/url.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/url/url.sty
|
||||
INPUT /usr/share/texmf-dist/tex/generic/bitset/bitset.sty
|
||||
INPUT /usr/share/texmf-dist/tex/generic/bitset/bitset.sty
|
||||
INPUT /usr/share/texmf-dist/tex/generic/bigintcalc/bigintcalc.sty
|
||||
INPUT /usr/share/texmf-dist/tex/generic/bigintcalc/bigintcalc.sty
|
||||
INPUT /usr/share/texmf-dist/tex/generic/atbegshi/atbegshi.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/base/atbegshi-ltx.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/base/atbegshi-ltx.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/hyperref/hpdftex.def
|
||||
INPUT /usr/share/texmf-dist/tex/latex/hyperref/hpdftex.def
|
||||
INPUT /usr/share/texmf-dist/tex/latex/hyperref/hpdftex.def
|
||||
INPUT /usr/share/texmf-dist/tex/latex/atveryend/atveryend.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/base/atveryend-ltx.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/base/atveryend-ltx.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/rerunfilecheck/rerunfilecheck.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/rerunfilecheck/rerunfilecheck.sty
|
||||
INPUT /usr/share/texmf-dist/tex/generic/uniquecounter/uniquecounter.sty
|
||||
INPUT /usr/share/texmf-dist/tex/generic/uniquecounter/uniquecounter.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/adjustbox/adjustbox.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/adjustbox/adjustbox.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/xkeyval/xkeyval.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/xkeyval/xkeyval.sty
|
||||
INPUT /usr/share/texmf-dist/tex/generic/xkeyval/xkeyval.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/xkeyval/xkvutils.tex
|
||||
INPUT /usr/share/texmf-dist/tex/latex/adjustbox/adjcalc.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/adjustbox/adjcalc.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/adjustbox/trimclip.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/adjustbox/trimclip.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/graphics/graphicx.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/graphics/graphicx.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/graphics/graphics.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/graphics/graphics.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/graphics/trig.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/graphics/trig.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/graphics-cfg/graphics.cfg
|
||||
INPUT /usr/share/texmf-dist/tex/latex/graphics-cfg/graphics.cfg
|
||||
INPUT /usr/share/texmf-dist/tex/latex/graphics-cfg/graphics.cfg
|
||||
INPUT /usr/share/texmf-dist/tex/latex/graphics-def/pdftex.def
|
||||
INPUT /usr/share/texmf-dist/tex/latex/graphics-def/pdftex.def
|
||||
INPUT /usr/share/texmf-dist/tex/latex/graphics-def/pdftex.def
|
||||
INPUT /usr/share/texmf-dist/tex/latex/collectbox/collectbox.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/collectbox/collectbox.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/adjustbox/tc-pdftex.def
|
||||
INPUT /usr/share/texmf-dist/tex/latex/adjustbox/tc-pdftex.def
|
||||
INPUT /usr/share/texmf-dist/tex/latex/adjustbox/tc-pdftex.def
|
||||
INPUT /usr/share/texmf-dist/tex/latex/ifoddpage/ifoddpage.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/ifoddpage/ifoddpage.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/ifoddpage/ifoddpage.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/varwidth/varwidth.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/varwidth/varwidth.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/varwidth/varwidth.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/footmisc/footmisc.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/footmisc/footmisc.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/amsmath/amsmath.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/amsmath/amsmath.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/amsmath/amsopn.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/amsmath/amstext.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/amsmath/amstext.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/amsmath/amsgen.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/amsmath/amsgen.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/amsmath/amsbsy.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/amsmath/amsbsy.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/amsmath/amsopn.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/amsfonts/amssymb.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/amsfonts/amssymb.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/amsfonts/amsfonts.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/amsfonts/amsfonts.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/mathtools/mathtools.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/mathtools/mathtools.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/tools/calc.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/tools/calc.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/mathtools/mhsetup.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/mathtools/mhsetup.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/float/float.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/float/float.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/fancyhdr/fancyhdr.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/fancyhdr/fancyhdr.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/caption/subcaption.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/caption/subcaption.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/caption/caption.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/caption/caption.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/caption/caption3.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/caption/caption3.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/lastpage/lastpage.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/lastpage/lastpage.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/lastpage/lastpage2e.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/lastpage/lastpage2e.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/lastpage/lastpagemodern.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/lastpage/lastpagemodern.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/listings/listings.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/listings/listings.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/listings/lstpatch.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/listings/lstpatch.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/listings/lstpatch.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/listings/lstmisc.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/listings/lstmisc.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/listings/lstmisc.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/listings/listings.cfg
|
||||
INPUT /usr/share/texmf-dist/tex/latex/listings/listings.cfg
|
||||
INPUT /usr/share/texmf-dist/tex/latex/listings/listings.cfg
|
||||
INPUT /usr/share/texmf-dist/tex/latex/todonotes/todonotes.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/todonotes/todonotes.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/base/ifthen.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/base/ifthen.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/xcolor/xcolor.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/xcolor/xcolor.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/graphics-cfg/color.cfg
|
||||
INPUT /usr/share/texmf-dist/tex/latex/graphics-cfg/color.cfg
|
||||
INPUT /usr/share/texmf-dist/tex/latex/graphics-cfg/color.cfg
|
||||
INPUT /usr/share/texmf-dist/tex/latex/graphics/mathcolor.ltx
|
||||
INPUT /usr/share/texmf-dist/tex/latex/graphics/mathcolor.ltx
|
||||
INPUT /usr/share/texmf-dist/tex/latex/graphics/mathcolor.ltx
|
||||
INPUT /usr/share/texmf-dist/tex/latex/pgf/frontendlayer/tikz.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/pgf/frontendlayer/tikz.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/pgf/basiclayer/pgf.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/pgf/basiclayer/pgf.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/pgf/utilities/pgfrcs.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/pgf/utilities/pgfrcs.sty
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/utilities/pgfutil-common.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/utilities/pgfutil-latex.def
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/utilities/pgfrcs.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/utilities/pgfrcs.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/utilities/pgfrcs.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/pgf.revision.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/pgf.revision.tex
|
||||
INPUT /usr/share/texmf-dist/tex/latex/pgf/basiclayer/pgfcore.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/pgf/basiclayer/pgfcore.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/pgf/systemlayer/pgfsys.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/pgf/systemlayer/pgfsys.sty
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsys.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsys.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsys.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/utilities/pgfkeys.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/utilities/pgfkeyslibraryfiltered.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgf.cfg
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsys-pdftex.def
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsys-pdftex.def
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsys-common-pdf.def
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsyssoftpath.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsyssoftpath.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsyssoftpath.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsysprotocol.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsysprotocol.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsysprotocol.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcore.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcore.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcore.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/math/pgfmath.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/math/pgfmathutil.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/math/pgfmathparser.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.basic.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.trigonometric.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.random.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.comparison.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.base.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.round.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.misc.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.integerarithmetics.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/math/pgfmathcalc.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfloat.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/math/pgfint.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepoints.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathconstruct.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathusage.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorescopes.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoregraphicstate.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoretransformations.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorequick.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreobjects.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathprocessing.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorearrows.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreshade.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreimage.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreexternal.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorelayers.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoretransparency.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepatterns.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorerdf.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/modules/pgfmoduleshapes.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/modules/pgfmoduleplot.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version-0-65.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version-0-65.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version-1-18.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version-1-18.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/pgf/utilities/pgffor.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/pgf/utilities/pgffor.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/pgf/utilities/pgfkeys.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/pgf/utilities/pgfkeys.sty
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/utilities/pgfkeys.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/utilities/pgfkeys.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/utilities/pgfkeys.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/latex/pgf/math/pgfmath.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/pgf/math/pgfmath.sty
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/math/pgfmath.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/math/pgfmath.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/math/pgfmath.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/utilities/pgffor.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/utilities/pgffor.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/utilities/pgffor.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/tikz.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/tikz.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/tikz.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/libraries/pgflibraryplothandlers.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/libraries/pgflibraryplothandlers.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/modules/pgfmodulematrix.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarytopaths.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarytopaths.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarypositioning.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarypositioning.code.tex
|
||||
INPUT /usr/share/texmf-dist/tex/latex/silence/silence.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/silence/silence.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/appendix/appendix.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/appendix/appendix.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/tocloft/tocloft.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/tocloft/tocloft.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/titlesec/titlesec.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/titlesec/titlesec.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/lipsum/lipsum.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/lipsum/lipsum.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/l3packages/l3keys2e/l3keys2e.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/l3packages/l3keys2e/l3keys2e.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/l3kernel/expl3.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/l3kernel/expl3.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def
|
||||
INPUT /usr/share/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def
|
||||
INPUT /usr/share/texmf-dist/tex/latex/lipsum/lipsum.ltd.tex
|
||||
INPUT /usr/share/texmf-dist/tex/latex/lipsum/lipsum.ltd.tex
|
||||
INPUT /usr/share/texmf-dist/tex/latex/pdfpages/pdfpages.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/pdfpages/pdfpages.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/eso-pic/eso-pic.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/eso-pic/eso-pic.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/pdfpages/pppdftex.def
|
||||
INPUT /usr/share/texmf-dist/tex/latex/pdfpages/pppdftex.def
|
||||
INPUT /usr/share/texmf-dist/tex/latex/pdfpages/pppdftex.def
|
||||
INPUT /usr/share/texmf-dist/tex/latex/csquotes/csquotes.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/csquotes/csquotes.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/csquotes/csquotes.def
|
||||
INPUT /usr/share/texmf-dist/tex/latex/csquotes/csquotes.def
|
||||
INPUT /usr/share/texmf-dist/tex/latex/csquotes/csquotes.def
|
||||
INPUT /usr/share/texmf-dist/tex/latex/csquotes/csquotes.cfg
|
||||
INPUT /usr/share/texmf-dist/tex/latex/csquotes/csquotes.cfg
|
||||
INPUT /usr/share/texmf-dist/tex/latex/csquotes/csquotes.cfg
|
||||
INPUT /usr/share/texmf-dist/tex/latex/cleveref/cleveref.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/cleveref/cleveref.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/outlines/outlines.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/outlines/outlines.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/biblatex/biblatex.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/biblatex/biblatex.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/logreq/logreq.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/logreq/logreq.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/logreq/logreq.def
|
||||
INPUT /usr/share/texmf-dist/tex/latex/logreq/logreq.def
|
||||
INPUT /usr/share/texmf-dist/tex/latex/logreq/logreq.def
|
||||
INPUT /usr/share/texmf-dist/tex/latex/biblatex/blx-dm.def
|
||||
INPUT /usr/share/texmf-dist/tex/latex/biblatex/blx-dm.def
|
||||
INPUT /usr/share/texmf-dist/tex/latex/biblatex/blx-dm.def
|
||||
INPUT /usr/share/texmf-dist/tex/latex/biblatex/blx-compat.def
|
||||
INPUT /usr/share/texmf-dist/tex/latex/biblatex/blx-compat.def
|
||||
INPUT /usr/share/texmf-dist/tex/latex/biblatex/blx-compat.def
|
||||
INPUT /usr/share/texmf-dist/tex/latex/biblatex/biblatex.def
|
||||
INPUT /usr/share/texmf-dist/tex/latex/biblatex/biblatex.def
|
||||
INPUT /usr/share/texmf-dist/tex/latex/biblatex/biblatex.def
|
||||
INPUT /usr/share/texmf-dist/tex/latex/biblatex/bbx/numeric.bbx
|
||||
INPUT /usr/share/texmf-dist/tex/latex/biblatex/bbx/numeric.bbx
|
||||
INPUT /usr/share/texmf-dist/tex/latex/biblatex/bbx/numeric.bbx
|
||||
INPUT /usr/share/texmf-dist/tex/latex/biblatex/bbx/standard.bbx
|
||||
INPUT /usr/share/texmf-dist/tex/latex/biblatex/bbx/standard.bbx
|
||||
INPUT /usr/share/texmf-dist/tex/latex/biblatex/bbx/standard.bbx
|
||||
INPUT /usr/share/texmf-dist/tex/latex/biblatex/cbx/numeric.cbx
|
||||
INPUT /usr/share/texmf-dist/tex/latex/biblatex/cbx/numeric.cbx
|
||||
INPUT /usr/share/texmf-dist/tex/latex/biblatex/cbx/numeric.cbx
|
||||
INPUT /usr/share/texmf-dist/tex/latex/biblatex/biblatex.cfg
|
||||
INPUT /usr/share/texmf-dist/tex/latex/biblatex/biblatex.cfg
|
||||
INPUT /usr/share/texmf-dist/tex/latex/biblatex/biblatex.cfg
|
||||
INPUT /usr/share/texmf-dist/tex/latex/biblatex/blx-case-expl3.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/biblatex/blx-case-expl3.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/l3packages/xparse/xparse.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/l3packages/xparse/xparse.sty
|
||||
INPUT build/main.aux
|
||||
INPUT build/main.aux
|
||||
INPUT build/main.aux
|
||||
OUTPUT build/main.aux
|
||||
INPUT /usr/share/texmf-dist/tex/latex/graphics/color.sty
|
||||
INPUT build/main.out
|
||||
INPUT build/main.out
|
||||
INPUT build/main.out
|
||||
INPUT build/main.out
|
||||
OUTPUT build/main.pdf
|
||||
INPUT build/main.out
|
||||
INPUT build/main.out
|
||||
OUTPUT build/main.out
|
||||
INPUT /usr/share/texmf-dist/tex/context/base/mkii/supp-pdf.mkii
|
||||
INPUT /usr/share/texmf-dist/tex/context/base/mkii/supp-pdf.mkii
|
||||
INPUT /usr/share/texmf-dist/tex/context/base/mkii/supp-pdf.mkii
|
||||
INPUT /usr/share/texmf-dist/tex/latex/epstopdf-pkg/epstopdf-base.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/epstopdf-pkg/epstopdf-base.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/latexconfig/epstopdf-sys.cfg
|
||||
INPUT /usr/share/texmf-dist/tex/latex/latexconfig/epstopdf-sys.cfg
|
||||
INPUT /usr/share/texmf-dist/tex/latex/latexconfig/epstopdf-sys.cfg
|
||||
INPUT /usr/share/texmf-dist/tex/latex/pdflscape/pdflscape.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/pdflscape/pdflscape.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/pdflscape/pdflscape-nometadata.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/pdflscape/pdflscape-nometadata.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/graphics/lscape.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/graphics/lscape.sty
|
||||
INPUT /usr/share/texmf-dist/tex/latex/biblatex/lbx/english.lbx
|
||||
INPUT /usr/share/texmf-dist/tex/latex/biblatex/lbx/english.lbx
|
||||
INPUT /usr/share/texmf-dist/tex/latex/biblatex/lbx/english.lbx
|
||||
OUTPUT build/main.bcf
|
||||
INPUT build/main.bbl
|
||||
INPUT build/main.bbl
|
||||
INPUT build/main.bbl
|
||||
INPUT build/main.bbl
|
||||
INPUT build/main.bbl
|
||||
INPUT /usr/share/texmf-dist/fonts/tfm/jknappen/ec/ecrm1728.tfm
|
||||
INPUT ./img/DTU.pdf
|
||||
INPUT ./img/DTU.pdf
|
||||
INPUT ./img/DTU.pdf
|
||||
INPUT ./img/DTU.pdf
|
||||
INPUT /usr/share/texmf-dist/fonts/tfm/jknappen/ec/ecrm2488.tfm
|
||||
INPUT /usr/share/texmf-dist/fonts/tfm/jknappen/ec/eccc2488.tfm
|
||||
INPUT /usr/share/texmf-dist/fonts/tfm/jknappen/ec/eccc1440.tfm
|
||||
INPUT /usr/share/texmf-dist/fonts/tfm/jknappen/ec/ecrm1200.tfm
|
||||
INPUT /usr/share/texmf-dist/fonts/tfm/public/cm/cmr12.tfm
|
||||
INPUT /usr/share/texmf-dist/fonts/tfm/public/cm/cmr8.tfm
|
||||
INPUT /usr/share/texmf-dist/fonts/tfm/public/cm/cmr6.tfm
|
||||
INPUT /usr/share/texmf-dist/fonts/tfm/public/cm/cmmi12.tfm
|
||||
INPUT /usr/share/texmf-dist/fonts/tfm/public/cm/cmmi8.tfm
|
||||
INPUT /usr/share/texmf-dist/fonts/tfm/public/cm/cmmi6.tfm
|
||||
INPUT /usr/share/texmf-dist/fonts/tfm/public/cm/cmsy10.tfm
|
||||
INPUT /usr/share/texmf-dist/fonts/tfm/public/cm/cmsy8.tfm
|
||||
INPUT /usr/share/texmf-dist/fonts/tfm/public/cm/cmsy6.tfm
|
||||
INPUT /usr/share/texmf-dist/fonts/tfm/public/cm/cmex10.tfm
|
||||
INPUT /usr/share/texmf-dist/fonts/tfm/public/amsfonts/cmextra/cmex8.tfm
|
||||
INPUT /usr/share/texmf-dist/fonts/tfm/public/amsfonts/cmextra/cmex7.tfm
|
||||
INPUT /usr/share/texmf-dist/tex/latex/amsfonts/umsa.fd
|
||||
INPUT /usr/share/texmf-dist/tex/latex/amsfonts/umsa.fd
|
||||
INPUT /usr/share/texmf-dist/tex/latex/amsfonts/umsa.fd
|
||||
INPUT /usr/share/texmf-dist/fonts/tfm/public/amsfonts/symbols/msam10.tfm
|
||||
INPUT /usr/share/texmf-dist/fonts/tfm/public/amsfonts/symbols/msam10.tfm
|
||||
INPUT /usr/share/texmf-dist/fonts/tfm/public/amsfonts/symbols/msam7.tfm
|
||||
INPUT /usr/share/texmf-dist/tex/latex/amsfonts/umsb.fd
|
||||
INPUT /usr/share/texmf-dist/tex/latex/amsfonts/umsb.fd
|
||||
INPUT /usr/share/texmf-dist/tex/latex/amsfonts/umsb.fd
|
||||
INPUT /usr/share/texmf-dist/fonts/tfm/public/amsfonts/symbols/msbm10.tfm
|
||||
INPUT /usr/share/texmf-dist/fonts/tfm/public/amsfonts/symbols/msbm10.tfm
|
||||
INPUT /usr/share/texmf-dist/fonts/tfm/public/amsfonts/symbols/msbm7.tfm
|
||||
INPUT /usr/share/texmf-dist/tex/generic/stringenc/se-pdfdoc.def
|
||||
INPUT /usr/share/texmf-dist/tex/generic/stringenc/se-pdfdoc.def
|
||||
INPUT /usr/share/texmf-dist/tex/generic/stringenc/se-pdfdoc.def
|
||||
INPUT /var/lib/texmf/fonts/map/pdftex/updmap/pdftex.map
|
||||
INPUT /usr/share/texmf-dist/fonts/enc/dvips/cm-super/cm-super-t1.enc
|
||||
INPUT /usr/share/texmf-dist/fonts/tfm/jknappen/ec/ecrm1440.tfm
|
||||
INPUT /usr/share/texmf-dist/fonts/tfm/public/amsfonts/cmextra/cmex7.tfm
|
||||
INPUT /usr/share/texmf-dist/fonts/tfm/public/amsfonts/cmextra/cmex7.tfm
|
||||
INPUT /usr/share/texmf-dist/fonts/tfm/public/amsfonts/symbols/msam10.tfm
|
||||
INPUT /usr/share/texmf-dist/fonts/tfm/public/amsfonts/symbols/msam7.tfm
|
||||
INPUT /usr/share/texmf-dist/fonts/tfm/public/amsfonts/symbols/msam5.tfm
|
||||
INPUT /usr/share/texmf-dist/fonts/tfm/public/amsfonts/symbols/msbm10.tfm
|
||||
INPUT /usr/share/texmf-dist/fonts/tfm/public/amsfonts/symbols/msbm7.tfm
|
||||
INPUT /usr/share/texmf-dist/fonts/tfm/public/amsfonts/symbols/msbm5.tfm
|
||||
INPUT ./img/dropout.png
|
||||
INPUT ./img/dropout.png
|
||||
INPUT ./img/dropout.png
|
||||
INPUT ./img/dropout.png
|
||||
INPUT ./img/dropout.png
|
||||
INPUT /usr/share/texmf-dist/fonts/tfm/jknappen/ec/ecbx1000.tfm
|
||||
INPUT /usr/share/texmf-dist/fonts/tfm/jknappen/ec/ecbi1000.tfm
|
||||
INPUT /usr/share/texmf-dist/fonts/tfm/jknappen/ec/ecbi0800.tfm
|
||||
INPUT /usr/share/texmf-dist/fonts/tfm/jknappen/ec/ecti1000.tfm
|
||||
INPUT /usr/share/texmf-dist/fonts/tfm/jknappen/ec/ecti0800.tfm
|
||||
INPUT ./img/DTU.pdf
|
||||
INPUT ./img/DTU.pdf
|
||||
INPUT ./img/DTU.pdf
|
||||
INPUT /usr/share/texmf-dist/tex/latex/base/t1cmtt.fd
|
||||
INPUT /usr/share/texmf-dist/tex/latex/base/t1cmtt.fd
|
||||
INPUT /usr/share/texmf-dist/tex/latex/base/t1cmtt.fd
|
||||
INPUT /usr/share/texmf-dist/fonts/tfm/jknappen/ec/ectt1000.tfm
|
||||
INPUT ./img/DTU.pdf
|
||||
INPUT ./img/DTU.pdf
|
||||
INPUT ./img/DTU.pdf
|
||||
INPUT build/main.aux
|
||||
INPUT build/main.out
|
||||
INPUT build/main.out
|
||||
INPUT build/main.run.xml
|
||||
OUTPUT build/main.run.xml
|
||||
INPUT /usr/share/texmf-dist/fonts/type1/public/amsfonts/cm/cmmi10.pfb
|
||||
INPUT /usr/share/texmf-dist/fonts/type1/public/cm-super/sfbi0800.pfb
|
||||
INPUT /usr/share/texmf-dist/fonts/type1/public/cm-super/sfcc1440.pfb
|
||||
INPUT /usr/share/texmf-dist/fonts/type1/public/cm-super/sfcc2488.pfb
|
||||
INPUT /usr/share/texmf-dist/fonts/type1/public/cm-super/sfrm1000.pfb
|
||||
INPUT /usr/share/texmf-dist/fonts/type1/public/cm-super/sfrm1200.pfb
|
||||
INPUT /usr/share/texmf-dist/fonts/type1/public/cm-super/sfti0800.pfb
|
||||
INPUT /usr/share/texmf-dist/fonts/type1/public/cm-super/sftt1000.pfb
|
||||
1172
build/main.log
1172
build/main.log
File diff suppressed because it is too large
Load Diff
|
|
@ -1,4 +0,0 @@
|
|||
\BOOKMARK [1][-]{section.1}{Test\040Case}{}% 1
|
||||
\BOOKMARK [1][-]{section.2}{Test\040Specification}{}% 2
|
||||
\BOOKMARK [1][-]{section.3}{Test\040Results}{}% 3
|
||||
\BOOKMARK [1][-]{section.4}{Discussion\040and\040Outlook}{}% 4
|
||||
BIN
build/main.pdf
BIN
build/main.pdf
Binary file not shown.
|
|
@ -1,82 +0,0 @@
|
|||
<?xml version="1.0" standalone="yes"?>
|
||||
<!-- logreq request file -->
|
||||
<!-- logreq version 1.0 / dtd version 1.0 -->
|
||||
<!-- Do not edit this file! -->
|
||||
<!DOCTYPE requests [
|
||||
<!ELEMENT requests (internal | external)*>
|
||||
<!ELEMENT internal (generic, (provides | requires)*)>
|
||||
<!ELEMENT external (generic, cmdline?, input?, output?, (provides | requires)*)>
|
||||
<!ELEMENT cmdline (binary, (option | infile | outfile)*)>
|
||||
<!ELEMENT input (file)+>
|
||||
<!ELEMENT output (file)+>
|
||||
<!ELEMENT provides (file)+>
|
||||
<!ELEMENT requires (file)+>
|
||||
<!ELEMENT generic (#PCDATA)>
|
||||
<!ELEMENT binary (#PCDATA)>
|
||||
<!ELEMENT option (#PCDATA)>
|
||||
<!ELEMENT infile (#PCDATA)>
|
||||
<!ELEMENT outfile (#PCDATA)>
|
||||
<!ELEMENT file (#PCDATA)>
|
||||
<!ATTLIST requests
|
||||
version CDATA #REQUIRED
|
||||
>
|
||||
<!ATTLIST internal
|
||||
package CDATA #REQUIRED
|
||||
priority (9) #REQUIRED
|
||||
active (0 | 1) #REQUIRED
|
||||
>
|
||||
<!ATTLIST external
|
||||
package CDATA #REQUIRED
|
||||
priority (1 | 2 | 3 | 4 | 5 | 6 | 7 | 8) #REQUIRED
|
||||
active (0 | 1) #REQUIRED
|
||||
>
|
||||
<!ATTLIST provides
|
||||
type (static | dynamic | editable) #REQUIRED
|
||||
>
|
||||
<!ATTLIST requires
|
||||
type (static | dynamic | editable) #REQUIRED
|
||||
>
|
||||
<!ATTLIST file
|
||||
type CDATA #IMPLIED
|
||||
>
|
||||
]>
|
||||
<requests version="1.0">
|
||||
<internal package="biblatex" priority="9" active="0">
|
||||
<generic>latex</generic>
|
||||
<provides type="dynamic">
|
||||
<file>main.bcf</file>
|
||||
</provides>
|
||||
<requires type="dynamic">
|
||||
<file>main.bbl</file>
|
||||
</requires>
|
||||
<requires type="static">
|
||||
<file>blx-dm.def</file>
|
||||
<file>blx-compat.def</file>
|
||||
<file>biblatex.def</file>
|
||||
<file>standard.bbx</file>
|
||||
<file>numeric.bbx</file>
|
||||
<file>numeric.cbx</file>
|
||||
<file>biblatex.cfg</file>
|
||||
<file>english.lbx</file>
|
||||
</requires>
|
||||
</internal>
|
||||
<external package="biblatex" priority="5" active="0">
|
||||
<generic>biber</generic>
|
||||
<cmdline>
|
||||
<binary>biber</binary>
|
||||
<infile>main</infile>
|
||||
</cmdline>
|
||||
<input>
|
||||
<file>main.bcf</file>
|
||||
</input>
|
||||
<output>
|
||||
<file>main.bbl</file>
|
||||
</output>
|
||||
<provides type="dynamic">
|
||||
<file>main.bbl</file>
|
||||
</provides>
|
||||
<requires type="dynamic">
|
||||
<file>main.bcf</file>
|
||||
</requires>
|
||||
</external>
|
||||
</requests>
|
||||
Binary file not shown.
|
|
@ -0,0 +1 @@
|
|||
Makes folder visible for git.
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1 @@
|
|||
Makes folder visible for git.
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
{"unit": "dumpload_sp", "value": 0.0, "time": 1718013928.6948164}
|
||||
{"unit": "dumpload_sp", "value": 20.0, "time": 1718013959.6977577}
|
||||
{"unit": "dumpload_sp", "value": 10.0, "time": 1718013989.6989655}
|
||||
{"unit": "dumpload_sp", "value": 0.0, "time": 1718014018.716735}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
#!./venv/bin/python3
|
||||
import syslab
|
||||
from json import dump
|
||||
from time import sleep, time
|
||||
|
||||
# Defines location and name of the measurements file
|
||||
LOG_FILE = f'data/measurements/measurements_{time():.00f}.json'
|
||||
print(f"Logging to file {LOG_FILE}")
|
||||
|
||||
# Set up a connection to the switchboards
|
||||
sb3192 = syslab.SwitchBoard('319-2')
|
||||
sb3193 = syslab.SwitchBoard('319-3')
|
||||
sb33012 = syslab.SwitchBoard("330-12")
|
||||
sb1172 = syslab.SwitchBoard('117-2')
|
||||
|
||||
# Convenience function to
|
||||
def take_measurements():
|
||||
measurements = {
|
||||
"pcc_p": sb3192.getActivePower('Grid'),
|
||||
"pcc_q": sb3192.getReactivePower('Grid'),
|
||||
"pv319_p": sb3192.getActivePower('PV'),
|
||||
"pv319_q": sb3192.getReactivePower('PV'),
|
||||
"dumpload_p": sb3192.getActivePower('Dumpload'),
|
||||
"dumpload_q": sb3192.getReactivePower('Dumpload'),
|
||||
"gaia_p": sb33012.getActivePower('Gaia'),
|
||||
"gaia_q": sb33012.getReactivePower('Gaia'),
|
||||
"pv330_p": sb33012.getActivePower('PV_1'),
|
||||
"pv330_q": sb33012.getReactivePower('PV_1'),
|
||||
"b2b_p": sb3193.getActivePower('ABB_Sec'),
|
||||
"b2b_q": sb3193.getReactivePower('ABB_Sec'),
|
||||
"battery_p": sb1172.getActivePower('Battery'),
|
||||
"battery_q": sb1172.getReactivePower('Battery'),
|
||||
}
|
||||
return [{'unit': k, 'value': meas.value, 'time': meas.timestampMicros/1e6} for k, meas in measurements.items()]
|
||||
|
||||
|
||||
while True:
|
||||
measurement = take_measurements()
|
||||
|
||||
# Open the output file in "append" mode which adds lines to the end
|
||||
with open(LOG_FILE, 'a') as file:
|
||||
for m in measurement:
|
||||
# Convert the dictionary m to a json string and put it
|
||||
# in the file.
|
||||
dump(m, file)
|
||||
# Write a newline for each measurement to make loading easier
|
||||
file.write('\n')
|
||||
sleep(1)
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
#!./venv/bin/python3
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
import json
|
||||
import matplotlib.pyplot as plt
|
||||
import os
|
||||
from datetime import timedelta
|
||||
|
||||
## Read the measurements data file ##
|
||||
DATA_MEAS_DIR = 'data/measurements'
|
||||
# Always plot latest datafile - replace [-1] with another index if you want to plot a specific file.
|
||||
MEAS_LOG_FILE = sorted(os.listdir(DATA_MEAS_DIR))[-1]
|
||||
|
||||
# Store each dictionary of the measurements json in a list
|
||||
with open(os.path.join(DATA_MEAS_DIR, MEAS_LOG_FILE)) as f:
|
||||
meas_data = [json.loads(line) for line in f]
|
||||
|
||||
# Use setpoint logger (only necessary for part two of the exercise "collecting fresh data")
|
||||
use_setpoint_log = False
|
||||
|
||||
|
||||
## Read the setpoints data file ##
|
||||
if use_setpoint_log:
|
||||
DATA_SP_DIR = 'data/setpoints'
|
||||
# Always plot latest datafile
|
||||
SP_LOG_FILE = sorted(os.listdir(DATA_SP_DIR))[-1]
|
||||
|
||||
# Store each dictionary of the setpoints json in a list
|
||||
with open(os.path.join(DATA_SP_DIR, SP_LOG_FILE)) as f:
|
||||
sp_data = [json.loads(line) for line in f]
|
||||
|
||||
# Merge measurements and setpoints in one list
|
||||
data = meas_data + sp_data
|
||||
|
||||
else:
|
||||
data = meas_data
|
||||
|
||||
################################################################################
|
||||
################## Part 2 ######################################################
|
||||
################################################################################
|
||||
|
||||
def overshoot(df, T1, T2):
|
||||
yT1, yT2 = df[T1], df[T2]
|
||||
over = 1 / (yT1 - yT2) * np.max(yT2 - df)
|
||||
return over
|
||||
|
||||
SETPOINT_UNIX = 1718013959.6977577
|
||||
SETPOINT_TS = pd.to_datetime(SETPOINT_UNIX, unit='s')
|
||||
WINDOW = pd.to_datetime(SETPOINT_UNIX+25, unit='s')
|
||||
|
||||
## The controller is reasonably fast at reacting to changes; the sum of in and
|
||||
## out is at zero roughly 5-10 seconds after a change.
|
||||
|
||||
# Construct a dataframe and pivot it to obtain a dataframe with a column per unit, and a row per timestamp.
|
||||
df = pd.DataFrame.from_records(data)
|
||||
df['time'] = pd.to_datetime(df['time'], unit='s')
|
||||
df_pivot = df.pivot_table(values='value', columns='unit', index='time')
|
||||
df_resampled = df_pivot.resample('0.1s').mean()
|
||||
df_resampled.interpolate(method='linear', inplace=True)
|
||||
df_resampled = pd.DataFrame(df_resampled)
|
||||
|
||||
# Plot the data. Note, that the data will mostly not be plotted with lines.
|
||||
plt.ion() # Turn interactive mode on
|
||||
plt.figure()
|
||||
ax1, ax2 = plt.subplot(211), plt.subplot(212)
|
||||
df_resampled[[c for c in df_resampled.columns if '_p' in c]].plot(marker='.', ax=ax1, linewidth=3)
|
||||
ax2.plot(df_resampled['pcc_p'][SETPOINT_TS:WINDOW], marker='.', linewidth=3, label='pcc_p')
|
||||
ax2.plot(df_resampled['dumpload_p'][SETPOINT_TS:WINDOW], marker='.', linewidth=3, label='dumpload')
|
||||
plt.legend()
|
||||
|
||||
# print(overshoot(df_resampled['pcc_p'][SETPOINT_TS:WINDOW], SETPOINT_TS, WINDOW))
|
||||
|
||||
plt.show(block=True)
|
||||
BIN
img/DTU.eps
BIN
img/DTU.eps
Binary file not shown.
BIN
img/DTU.pdf
BIN
img/DTU.pdf
Binary file not shown.
BIN
img/dropout.png
BIN
img/dropout.png
Binary file not shown.
|
Before Width: | Height: | Size: 583 KiB |
129
main.tex
129
main.tex
|
|
@ -1,129 +0,0 @@
|
|||
%! TEX root = main.tex
|
||||
\input{preamble.tex}
|
||||
\begin{document}
|
||||
\maketitle
|
||||
\thispagestyle{empty}
|
||||
\newpage
|
||||
|
||||
The following sections will concern the fourth test, \enquote{Supervisor
|
||||
Controller Dropout}.
|
||||
|
||||
\section{Test Case}
|
||||
\label{sec:test_case}
|
||||
|
||||
The purpose of this was to observe the system response to one of the failure
|
||||
modes to which we set out to become (more) resilient. As written in the group
|
||||
report, all of the controller units, as well as the supervisor, was activated
|
||||
during the test. Wind production was also active. As such, all units were under
|
||||
observation, however specifically the system daemon was the object under
|
||||
investigation, as this was our way of implementing a system response to failure
|
||||
in the form of loss of a controller. The system daemon should be passive during
|
||||
normal modes of operation, only receiving supervisor heartbeats, and should only
|
||||
kick into action when failure occurs.
|
||||
|
||||
Our use case, as specified in the group report, concerns the hybrid power plant
|
||||
as a means of frequency regulation with compensation for the fluctuations
|
||||
normally associated with renewable energy production. Therefore, the system
|
||||
should not be disruptive in case of a single controller breaking down; if that
|
||||
were the case, a single operator mistake could be rather costly to the rest of
|
||||
the grid participants.
|
||||
|
||||
Because of these concerns, the main metrics with which the outcome is measured
|
||||
is the $ RMSE $ from the expected frequency response during a supervisor
|
||||
controller dropout, as well as a more qualitative assessment of the system
|
||||
reaction time to a fault.
|
||||
|
||||
\section{Test Specification}
|
||||
\label{sec:test_spec}
|
||||
|
||||
This test begins with the system in a normal operating state. The individual
|
||||
unit controllers, as well as the supervisor controller, are started. The
|
||||
supervisor controller should then be given some amount of time (in this case
|
||||
15-20 seconds) to calculate a PCC baseline based on the baseline load combined
|
||||
with median PV production. Once this has been established, regular frequency
|
||||
data should be published to the controller.
|
||||
|
||||
After the supervisor has had a chance to behave somewhat normally - in our run,
|
||||
10 seconds after baseline establishing, the controller process is interrupted.
|
||||
The unit controllers are not directly investigated during this step, however
|
||||
their reaction to the lack of frequency data and split points should be apparent
|
||||
in the general system response. To stress the system further, the battery
|
||||
controller is also dropped after the supervisor has been ressurrected.
|
||||
|
||||
The output of the test, from which the $ RMSE $ is calculated, is the deviance
|
||||
in the FCR activation from the FCR setpoint, in the span of the test.
|
||||
|
||||
\section{Test Results}
|
||||
\label{sec:test_results}
|
||||
|
||||
\begin{figure}[H]
|
||||
\centering
|
||||
\includegraphics[width=0.8\textwidth]{img/dropout.png}
|
||||
\caption{Results from the dropout test}
|
||||
\label{fig:results}
|
||||
\end{figure}
|
||||
|
||||
The above \cref{fig:results} shows the results from the test. The two
|
||||
dotted, horizontal, black lines specify the times at which controllers were
|
||||
dropped; first the supervisor, then the battery controller. The dotted red line
|
||||
is when the system daemon notices the absence of the supervisor, and revives it
|
||||
by spawning a new process.
|
||||
|
||||
From the $ RMSE $ value, it seems that the system responds rather well to the
|
||||
supervisor being killed. Further inspection shows that the system shows signs of
|
||||
instability in the period immediately following the controllers being dropped,
|
||||
as should be expected, since it takes some time for the other components to mark
|
||||
the supervisor as deceased. However, it seems that the system stabilizes after a
|
||||
while with only minor fluctuations which could be from the wind turbine, since
|
||||
that is out of our control.
|
||||
|
||||
More results can be found in our GitLab repository. In the subfolder
|
||||
|
||||
\texttt{testing/final\_tests\_and\_results/dropout/console\_logs}
|
||||
|
||||
the complete logs from the three unit controllers, the supervisor, and the
|
||||
system daemon. It should be noted that when the supervisor is killed, the
|
||||
console output ends; this is since the system daemon, after a set timeout,
|
||||
revives the supervisor by spawning a new subprocess. This subprocess will then
|
||||
be a child of the system daemon, which itself is a child of a specific terminal.
|
||||
Therefore, the console output of the newly spawned supervisor is printed to the
|
||||
terminal containing the system daemon. This is the same for the battery
|
||||
controller. During this test, neither the PV- or load controllers were touched,
|
||||
so these have continuing output all throughout the test.
|
||||
|
||||
\section{Discussion and Outlook}
|
||||
\label{sec:discussion}
|
||||
|
||||
To conclude our test of controller dropout, the system seems to handle a
|
||||
controller dropout reasonably well. This failure mode is a last-ditch effort in
|
||||
case of absolute emergency; only in extreme circumstances would it be expected
|
||||
for a controller to drop out spontaneously. It is, however, rather comforting to
|
||||
know that should it happen, the system seems to stabilize after a short while.
|
||||
|
||||
There is, however, room for improvement. The controllers seem to react violently
|
||||
in the first phases of a supervisor dropout. This may not be able to be
|
||||
mitigated, however it could be an area of interest.
|
||||
|
||||
As was also apparent from our other tests, the controllers seem to behave in a
|
||||
competing way, sometimes over-shadowing the other unit controllers. By phasing
|
||||
in controller response following e.g. a sigmoid curve, the system response could
|
||||
perhaps be modified to be a more gentle, gradual reaction.
|
||||
|
||||
It should also be noted that this way of spawning new processes only works by
|
||||
spawning said child process on the machine running the system daemon. This
|
||||
detail could of course be changed by changing the mechanism of spawning a
|
||||
controller, however it is a definite limitation of the current design. Due to
|
||||
time constraints, it was not further developed, as the principle of reviving
|
||||
controllers was more important than that controller running on a specific
|
||||
machine.
|
||||
|
||||
A next test, which could yield interesting results and conclusions, could be to
|
||||
observe how the system were to react in case of temporary controller dropouts,
|
||||
where the dropped controller came back to life. In this case, the system daemon
|
||||
would probably respond by creating another controller, which would then compete
|
||||
with the old one, however the exact result cannot be recognized without running
|
||||
the test. It could also be interesting to see how the system would act in case
|
||||
of these dropouts happening more frequently, or during periods of frequency
|
||||
volatility.
|
||||
|
||||
\end{document}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
#!./venv/bin/python3
|
||||
import pandas as pd
|
||||
import json
|
||||
import matplotlib.pyplot as plt
|
||||
import os
|
||||
|
||||
DATA_MEAS_DIR = 'data/measurements'
|
||||
SPECIFIC_FILE = ''
|
||||
MEAS_LOG_FILE = sorted(os.listdir(DATA_MEAS_DIR))[-1] if not SPECIFIC_FILE else SPECIFIC_FILE
|
||||
|
||||
with open(os.path.join(DATA_MEAS_DIR, MEAS_LOG_FILE)) as f:
|
||||
meas_data = [json.loads(line) for line in f]
|
||||
data = meas_data
|
||||
|
||||
df = pd.DataFrame.from_records(data)
|
||||
df['time'] = pd.to_datetime(df['time'], unit='s')
|
||||
df_pivot = df.pivot_table(values='value', columns='unit', index='time')
|
||||
df_resampled = df_pivot.resample('s').mean()
|
||||
df_resampled.interpolate(method='linear', inplace=True)
|
||||
df_resampled = pd.DataFrame(df_resampled)
|
||||
|
||||
# Plot the data. Note, that the data will mostly not be plotted with lines.
|
||||
plt.ion() # Turn interactive mode on
|
||||
plt.figure()
|
||||
ax1, ax2 = plt.subplot(211), plt.subplot(212)
|
||||
df_resampled[[c for c in df_resampled.columns if '_p' in c]].plot(marker='.', ax=ax1, linewidth=3)
|
||||
df_resampled[[c for c in df_resampled.columns if '_q' in c]].plot(marker='.', ax=ax2, linewidth=3)
|
||||
plt.show(block=True)
|
||||
142
preamble.tex
142
preamble.tex
|
|
@ -1,142 +0,0 @@
|
|||
%! TEX root = main.tex
|
||||
\documentclass[a4paper, english]{article}
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%%%%%%%%% Variables & commands prone to change
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
\newcommand{\titl}{Individual Final Report}
|
||||
\newcommand{\auth}{Daniel Brasholt s214676}
|
||||
\newcommand{\courseno}{46045}
|
||||
\newcommand{\course}{Design and Implementation\\ of Coordinated Distributed Energy Systems}
|
||||
\newcommand{\dato}{June 2024}
|
||||
\newcommand{\secheader}{Section}
|
||||
\newcommand{\subsecheader}{Part}
|
||||
\title{\includegraphics[width=.15\textwidth]{DTU}\\
|
||||
\vspace{.5em}\Huge\scshape\titl\\
|
||||
\vspace{-4mm}\rule{4cm}{0.5mm}\\
|
||||
\Large{\courseno\ \course}}
|
||||
\author{\auth}
|
||||
\date{\dato}
|
||||
% \newcommand{\sourcefile}{}
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%%%%%%%%% Packages & Geometry
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
\usepackage[
|
||||
a4paper,
|
||||
top=2cm,
|
||||
bottom=2cm,
|
||||
left=2cm,
|
||||
right=2cm,
|
||||
marginparwidth=2cm,
|
||||
headheight=10mm,
|
||||
footskip=10mm
|
||||
]{geometry}
|
||||
|
||||
\usepackage[T1]{fontenc}
|
||||
\usepackage[utf8]{inputenc}
|
||||
\usepackage[pdfencoding=auto, psdextra]{hyperref}
|
||||
\usepackage[export]{adjustbox}
|
||||
\usepackage[bottom]{footmisc}
|
||||
% \usepackage[dansk]{babel}
|
||||
\usepackage{
|
||||
amsmath,
|
||||
amssymb,
|
||||
mathtools,
|
||||
graphicx,
|
||||
float,
|
||||
fancyhdr,
|
||||
subcaption,
|
||||
lastpage,
|
||||
listings,
|
||||
todonotes,
|
||||
silence,
|
||||
appendix,
|
||||
tocloft,
|
||||
titlesec,
|
||||
lipsum,
|
||||
pdfpages,
|
||||
csquotes,
|
||||
cleveref,
|
||||
outlines
|
||||
}
|
||||
|
||||
\usepackage[
|
||||
format=plain,
|
||||
labelfont={bf,it,footnotesize},
|
||||
textfont={it,footnotesize}
|
||||
]{caption}
|
||||
|
||||
\usepackage[
|
||||
backend=biber,
|
||||
style=numeric,
|
||||
sorting=nty
|
||||
]{biblatex}
|
||||
% \addbibresource{\sourcefile}
|
||||
|
||||
\hypersetup{
|
||||
colorlinks = true,
|
||||
urlcolor = blue,
|
||||
linkcolor = blue,
|
||||
citecolor = red
|
||||
}
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%%%%%%%%% Equations
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
\renewcommand{\theequation}{\arabic{section}.\arabic{equation}}
|
||||
\numberwithin{equation}{section}
|
||||
\DeclarePairedDelimiter{\paren}{(}{)}
|
||||
\DeclarePairedDelimiter\ceil{\lceil}{\rceil}
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%%%%%%%%% Figures and sections
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
\renewcommand{\thesection}{\arabic{section}}
|
||||
\renewcommand{\thesubsection}{\arabic{section}.\arabic{subsection}}
|
||||
\renewcommand{\thesubsubsection}{\arabic{section}.\arabic{subsection}.\alph{subsubsection}}
|
||||
\Crefname{section}{\secheader}{\secheader}
|
||||
\graphicspath{{./img}}
|
||||
|
||||
% Section
|
||||
\titleformat{\section}[block]
|
||||
{\normalfont\Large\scshape\filright}{\fbox{\secheader\ \thesection}}{1em}{}
|
||||
|
||||
% Subsection
|
||||
\titleformat{\subsection}
|
||||
{\titlerule
|
||||
\vspace{.8ex}%
|
||||
\normalfont}
|
||||
{\subsecheader\ \thesubsection:}{1em}{}
|
||||
|
||||
% Subsubsection
|
||||
\titleformat{\subsubsection}
|
||||
{}{\thesubsubsection)}{1em}{}
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%%%%%%%%% Page & footer styling
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
\pagestyle{fancy}
|
||||
\lhead{\titl \\ \auth}
|
||||
\chead{\includegraphics[width=.05\textwidth]{DTU}}
|
||||
\rhead{\courseno \ \course}
|
||||
\cfoot{Page \thepage\, of \pageref*{LastPage}}
|
||||
\renewcommand{\headrulewidth}{0.4pt}
|
||||
\renewcommand{\footrulewidth}{0.4pt}
|
||||
\setlength{\headheight}{36.75034pt}
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%%%%%%%%% ToC styling
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
\pagenumbering{arabic}
|
||||
\setcounter{tocdepth}{2}
|
||||
\tocloftpagestyle{fancy}
|
||||
\setlength{\cftsubsecnumwidth}{42pt}
|
||||
\addtolength{\cftsecnumwidth}{47pt}
|
||||
\addtolength{\cftsubsecnumwidth}{28pt}
|
||||
\addtocontents{toc}{~\hfill\textbf{Page}\par}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
beautifulsoup4==4.12.3
|
||||
certifi==2024.6.2
|
||||
charset-normalizer==3.3.2
|
||||
greenlet==3.0.3
|
||||
idna==3.7
|
||||
msgpack==1.0.8
|
||||
pynvim==0.5.0
|
||||
requests==2.32.3
|
||||
soupsieve==2.5
|
||||
# Editable Git install with no remote (syslab==0.3.0)
|
||||
-e /home/daniel/Dropbox/DTU/F24/46045/syslab/syslab-python
|
||||
urllib3==2.2.1
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# celery beat schedule file
|
||||
celerybeat-schedule
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
|
||||
\.DS_Store
|
||||
|
||||
# Pycharm
|
||||
.idea/
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
# SYSLAB Python Interface
|
||||
|
||||
This project provides a Python interface to several SYSLAB components.
|
||||
|
||||
## Required software
|
||||
|
||||
- Python (>=3.7)
|
||||
- python-requests (>=2.18)
|
||||
- python-bs4 (>= 4.7)
|
||||
|
||||
## Installation
|
||||
|
||||
To install the package in your local path, run
|
||||
|
||||
```shell
|
||||
python setup.py install
|
||||
```
|
||||
|
||||
Alternatively, copy the `syslab` folder into your project directory.
|
||||
|
||||
# Contributors
|
||||
|
||||
- Anders Thavlov: Initial implementation
|
||||
- Oliver Gehrke
|
||||
- Daniel Esteban Morales Bondy
|
||||
- Tue Vissing Jensen
|
||||
- Federico Zarelli
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
from syslab import SwitchBoard
|
||||
|
||||
name = '319-2'
|
||||
SB_connection = SwitchBoard(name)
|
||||
print("Let's look at what is going on in the switchboard {}.".format(name))
|
||||
|
||||
for bay in range(SB_connection.getNumBays()):
|
||||
print(SB_connection.getBayName(bay),' : ',SB_connection.getActivePower(bay))
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
# Notes on SOAP implementation
|
||||
|
||||
- Units should reflect SOAP methods onto their own namespace (1)
|
||||
- CompositeMeasurement should convert to/from SOAP
|
||||
- Type checking via client.get\_type('ns0:compositeMeasurement').elements
|
||||
-
|
||||
|
||||
# Notes about this module - to be discussed
|
||||
|
||||
SYSLAB\_Unit.py has a whole bunch of static methods - should these be split into a util library instead?
|
||||
Generally, many places where static methods are used for things that should perhaps just be functions...
|
||||
|
||||
The following files are empty:
|
||||
- BattOpMode.py
|
||||
- FlowBatteryState.py
|
||||
- GaiaWindTurbine.py
|
||||
|
||||
|
||||
To check the methods available, use, e.g.:
|
||||
http://syslab-33.syslab.dk:8080/typebased_WebService_HeatSubstation/HeatSwitchboardWebService/716-h1/resourceNames
|
||||
|
||||
To figure out this URL, look at software.xml for the corresponding machine, and use this template:
|
||||
|
||||
http://(machineName).syslab.dk:(port)/(interfaceName)/(shortServerName)/(unitname)/resourceNames
|
||||
|
||||
| field | corresponds to | notes |
|
||||
| ----- | -------------- | ----- |
|
||||
| machineName | N/A | Look this up on the wiki |
|
||||
| port | N/A | Dynamically allocated, starting at 8080 - good luck! |
|
||||
| interfaceName | typeBasedWebService, interfaceName | |
|
||||
| shortServerName | typeBasedWebService, serverClass | Remove the "Server" at the end |
|
||||
| unitname | dataLogger, unit | Also defined as "name" in hardware.xml |
|
||||
|
||||
|
||||
-------------------------
|
||||
|
||||
|
||||
SYSLAB COMMON
|
||||
Broadcast event logger:
|
||||
|
||||
Transcode to python:
|
||||
https://git.elektro.dtu.dk/syslab/syslab-common/-/blob/master/src/main/java/risoe/syslab/comm/broadcast/BroadcastLogSender.java
|
||||
|
||||
broadcast log sender
|
||||
:: transcode the "send()" method to python. It byte-encodes the message for UDP.
|
||||
Java:
|
||||
send(String origin, byte[] origIP, long timestamp, int ploadType, String message,
|
||||
int level, int flags, String[] tags)
|
||||
------------
|
||||
Python:
|
||||
----------.
|
||||
def send(origin, origIP, timestamp, ploadType, message, level, flags, tags):
|
||||
ploadbytes = message[:min(1024, len(message))].encode()
|
||||
origbytes = origin[:min(32, len(origin))].encode()
|
||||
tagbytes = tagsToBytes(tags, 256)
|
||||
pktlen = 2 + 2 + 1 + len(origbytes) + 4 + 2 + 2 + 8 + 1 + 2 + len(ploadbytes) + len(tagbytes)
|
||||
buf = bytearray(pktlen)
|
||||
buf[0] = BroadcastLogConstants.BROADCASTLOG_PKTID >> 8
|
||||
buf[1] = BroadcastLogConstants.BROADCASTLOG_PKTID & 0xff
|
||||
buf[2] = (pktlen >> 8) & 0xff
|
||||
buf[3] = pktlen & 0xff
|
||||
buf[4] = len(origbytes)
|
||||
buf[5:5+len(origbytes)] = origbytes
|
||||
writePtr = 5 + len(origbytes)
|
||||
buf[writePtr:writePtr+4] = origIP
|
||||
writePtr += 4
|
||||
buf[writePtr] = (level >> 8) & 0xff
|
||||
buf[writePtr+1] = level & 0xff
|
||||
buf[writePtr+2] = (flags >> 8) & 0xff
|
||||
buf[writePtr+3] = flags & 0xff
|
||||
for i in range(8):
|
||||
buf[writePtr+7-i] = timestamp & 0xff
|
||||
timestamp >>= 8
|
||||
writePtr += 8
|
||||
buf[writePtr] = ploadType & 0xff
|
||||
buf[writePtr+1] = (len(ploadbytes) >> 8) & 0xff
|
||||
buf[writePtr+2] = len(ploadbytes) & 0xff
|
||||
buf[writePtr+3:writePtr+3+len(ploadbytes)] = ploadbytes
|
||||
writePtr += len(ploadbytes)
|
||||
buf[writePtr:writePtr+len(tagbytes)] = tagbytes
|
||||
pack = n
|
||||
pack = DatagramPacket(buf, len(buf), InetAddress.getByName("localhost"), 4445)
|
||||
sock.send(pack)
|
||||
|
||||
|
||||
------------
|
||||
|
||||
broadcast log receiver
|
||||
+ needs a logger
|
||||
listener ist interface for receiver
|
||||
|
||||
gui wall (SYSLAB Userspacce)
|
||||
broadcast log displet
|
||||
https://git.elektro.dtu.dk/syslab/syslab-userspace/-/blob/master/src/main/java/risoe/syslab/gui/wall/displets/BroadcastLogDisplet.java
|
||||
... maybe extend with simple log file writer.
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
requests >= 2.18
|
||||
beautifulsoup4 >= 3.7
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
[flake8]
|
||||
ignore =
|
||||
max-line-length = 79
|
||||
max-complexity = 11
|
||||
|
||||
[pytest]
|
||||
addopts = --doctest-glob="*.rst"
|
||||
|
||||
[wheel]
|
||||
universal = True
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
from setuptools import setup, find_packages
|
||||
|
||||
|
||||
setup(
|
||||
name='syslab',
|
||||
version='0.3.0',
|
||||
author='Tue Vissing Jensen',
|
||||
author_email='tvjens at elektro.dtu.dk',
|
||||
description=('SYSLAB webservice client library.'),
|
||||
long_description=(''),
|
||||
url='https://www.syslab.dk',
|
||||
install_requires=[
|
||||
'requests>=2.18',
|
||||
'beautifulsoup4>=3.7',
|
||||
],
|
||||
packages=find_packages(exclude=['tests*']),
|
||||
include_package_data=True,
|
||||
entry_points={
|
||||
'console_scripts': [
|
||||
],
|
||||
},
|
||||
classifiers=[
|
||||
'Development Status :: 3 - Alpha',
|
||||
'Environment :: Console',
|
||||
'Intended Audience :: Science/Research',
|
||||
'License :: Other/Proprietary License',
|
||||
'Natural Language :: English',
|
||||
'Operating System :: OS Independent',
|
||||
'Programming Language :: Python',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Programming Language :: Python :: 3.7',
|
||||
'Programming Language :: Python :: 3.8',
|
||||
'Topic :: Scientific/Engineering',
|
||||
'Topic :: Software Development :: Libraries :: Python Modules',
|
||||
],
|
||||
)
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
SYSLAB library
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
SYSLAB is a Python 3 library for control and monitoring of units in SYSLAB. Usage of this library requires the running
|
||||
computer to be located in the SYSLAB network, either physically or virtually, ie. using a VPN connection.
|
||||
|
||||
|
||||
"""
|
||||
|
||||
__title__ = 'syslab'
|
||||
__version__ = '0.3.0'
|
||||
__author__ = 'Anders Thavlov, Daniel Esteban Morales Bondy, Tue Vissing Jensen'
|
||||
|
||||
__maintainer__ = 'Tue Vissing Jensen'
|
||||
__license__ = ''
|
||||
__copyright__ = 'DTU'
|
||||
|
||||
# Raise error if we are not on Python 3
|
||||
import sys
|
||||
if not (sys.version_info.major == 3 and sys.version_info.minor >= 5):
|
||||
raise RuntimeError("Must be using at least Python 3.5")
|
||||
|
||||
# DataTypes
|
||||
from .core.datatypes import CompositeMeasurement
|
||||
from .core.datatypes import CompositeBoolean
|
||||
|
||||
# Physical units
|
||||
from .physical import \
|
||||
SwitchBoard, \
|
||||
Dumpload, \
|
||||
Photovoltaics, \
|
||||
Battery, \
|
||||
HeatSwitchBoard, \
|
||||
WindTurbine, \
|
||||
DieselGenerator, \
|
||||
B2BConverter, \
|
||||
MeteoMast, \
|
||||
EVSE
|
||||
|
||||
# Logging utilities
|
||||
#from .comm.LogUtils import e
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
###############################################################
|
||||
# SYSLAB remote logging utilities v0.9
|
||||
# collect and record log events
|
||||
#
|
||||
# Author: Kai Heussen
|
||||
# Date: 2023/06/18
|
||||
###############################################################
|
||||
import logging
|
||||
import logging.handlers
|
||||
import time
|
||||
import syslab.config as config
|
||||
|
||||
event_log_formats = '%(asctime)s - %(name)s - %(levelname)s - %(filename)s - %(funcName)s - %(message)s'
|
||||
simple_log_formats = '%(asctime)s - %(name)s - %(message)s'
|
||||
|
||||
def setup_event_logger(loggername=config.EVLOG_NAME, host='localhost', port=config.REMOTE_PORT_EV, level=logging.INFO, formats=simple_log_formats): # UDP
|
||||
handler1 = logging.handlers.DatagramHandler(config.REMOTE_IP_EV, config.REMOTE_PORT_EV)
|
||||
handler2 = logging.handlers.DatagramHandler(host, port)
|
||||
handler3 = logging.StreamHandler()
|
||||
formatter = logging.Formatter(formats)
|
||||
handler3.setFormatter(formatter)
|
||||
loggr = logging.getLogger(loggername)
|
||||
loggr.setLevel(level)
|
||||
loggr.addHandler(handler1)
|
||||
loggr.addHandler(handler2)
|
||||
loggr.addHandler(handler3)
|
||||
return loggr
|
||||
|
||||
|
||||
def setup_local_logger(loggername=config.EVLOG_NAME, host='localhost', port=config.REMOTE_PORT_EV, level=logging.INFO, formats=simple_log_formats): # UDP
|
||||
handler = logging.handlers.DatagramHandler(host, port)
|
||||
formatter = logging.Formatter(formats)
|
||||
handler.setFormatter(formatter)
|
||||
loggr = logging.getLogger(loggername)
|
||||
loggr.setLevel(level)
|
||||
loggr.addHandler(handler)
|
||||
return loggr
|
||||
|
||||
|
||||
def setup_udp_logger(loggername=config.SPLOG_NAME, host=config.REMOTE_IP_SP, port=config.REMOTE_PORT_SP, level=logging.INFO, formats=config.LOG_FORMATS): # UDP
|
||||
handler = logging.handlers.DatagramHandler(host, port)
|
||||
formatter = logging.Formatter(formats)
|
||||
handler.setFormatter(formatter)
|
||||
loggr = logging.getLogger(loggername)
|
||||
loggr.setLevel(level)
|
||||
loggr.addHandler(handler)
|
||||
return loggr
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig(level=logging.DEBUG,
|
||||
format='%(asctime)s %(levelname)-8s %(message)s')
|
||||
|
||||
# logging.getLogger().addHandler(logging.handlers.DatagramHandler('10.42.242.3', 51010))
|
||||
logger = setup_local_logger()
|
||||
while True:
|
||||
logger.debug("This shouldn't show up")
|
||||
logger.info("This should show up")
|
||||
time.sleep(3)
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
#from BroadcastLogger import BroadcastLogSender
|
||||
#from LogUtils import setup_udp_logger
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
###############################################################
|
||||
# SYSLAB remote logger v0.9
|
||||
# utility to display and record log events
|
||||
# usage: move bash to target log directory, then call
|
||||
# python <path to syslab-python>/syslab/comm/logrec.py
|
||||
#
|
||||
# Author: Kai Heussen
|
||||
# Date: 2023/06/18
|
||||
###############################################################
|
||||
import pickle
|
||||
import logging
|
||||
import socket
|
||||
from syslab import config
|
||||
# import sys
|
||||
from time import time
|
||||
# from json import dump
|
||||
|
||||
BASIC = True
|
||||
FILE = True
|
||||
FILE_RAW = False # TODO: write a JSON formatter - based on https://stackoverflow.com/questions/50144628/python-logging-into-file-as-a-dictionary-or-json
|
||||
|
||||
logtype = "EV"
|
||||
time=time()
|
||||
logfile =f"syslab_{logtype}_log_{time:.00f}.txt"
|
||||
logfile_raw =f"syslab_{logtype}_log_{time:.00f}.raw" # not yet json
|
||||
|
||||
DEFAULT_PORT = config.REMOTE_PORT_SP if logtype == "SP" else config.REMOTE_PORT_EV
|
||||
|
||||
port = DEFAULT_PORT
|
||||
|
||||
#simple_formats = '%(asctime)s - %(name)s - %(message)s'
|
||||
simple_formats = '%(asctime)s - %(module)s - %(message)s'
|
||||
#formats='foo: %(levelname)s - %(module)s.%(funcName)s - %(message)s'
|
||||
event_log_formatter = '%(asctime)s - %(name)s - %(levelname)s - %(filename)s - %(module)s - %(funcName)s - %(message)s'
|
||||
|
||||
formats = simple_formats
|
||||
formatter = logging.Formatter(formats)
|
||||
|
||||
if BASIC:
|
||||
logging.basicConfig(format=formats)
|
||||
else:
|
||||
handler = logging.StreamHandler()
|
||||
handler.setLevel(logging.DEBUG)
|
||||
handler.setFormatter(formatter)
|
||||
logging.getLogger().addHandler(handler)
|
||||
|
||||
if FILE:
|
||||
handler = logging.FileHandler(logfile)
|
||||
handler.setLevel(logging.INFO)
|
||||
handler.setFormatter(formatter)
|
||||
logging.getLogger().addHandler(handler)
|
||||
|
||||
|
||||
logger = logging.getLogger()
|
||||
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
s.bind(('', port))
|
||||
|
||||
print(f'Listening for log records on port {port}...')
|
||||
if FILE:
|
||||
print(f'recording log entries in file: {logfile}')
|
||||
if FILE_RAW:
|
||||
print(f'recording raw log entries in file: {logfile_raw}')
|
||||
|
||||
try:
|
||||
while True:
|
||||
d, _ = s.recvfrom(1024)
|
||||
msg = pickle.loads(d[4:])
|
||||
logrec = logging.makeLogRecord(msg)
|
||||
logger.handle(logrec)
|
||||
if FILE_RAW:
|
||||
with open(logfile_raw, 'a') as file:
|
||||
# dump(logrec, file) # requires a JSON formatter
|
||||
file.write(f'{logrec.__str__() }\n') # Write a newline for each measurement to make loading easier
|
||||
#print(log)
|
||||
finally:
|
||||
s.close()
|
||||
if FILE_RAW:
|
||||
file.close()
|
||||
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
DEBUG = False
|
||||
SPLOG_NAME = 'SetPointLogger'
|
||||
EVLOG_NAME = 'EventLog'
|
||||
REMOTE_LOGGER = True
|
||||
REMOTE_IP_SP = '10.42.242.3' # UI Machine 03
|
||||
REMOTE_IP_EV = '10.42.242.3' # UI Machine 03
|
||||
REMOTE_PORT_SP = 51010
|
||||
REMOTE_PORT_EV = 51020
|
||||
LOCAL_IP = 'localhost'
|
||||
LOCAL_PORT_SP = 51010
|
||||
LOCAL_PORT_EV = 51020
|
||||
LOCAL_LOGGER = False
|
||||
LOG_FORMATS = '%(asctime)s - %(name)s - %(levelname)s - %(message)s' # '%(asctime)s %(levelname)-8s %(message)s'
|
||||
TEST_LOCAL = False
|
||||
if TEST_LOCAL:
|
||||
REMOTE_PORT_SP = LOCAL_PORT_SP
|
||||
REMOTE_PORT_EV = LOCAL_PORT_EV
|
||||
REMOTE_IP_SP = LOCAL_IP
|
||||
REMOTE_IP_EV = LOCAL_IP
|
||||
|
|
@ -0,0 +1,306 @@
|
|||
from .datatypes import CompositeBoolean, BattOpMode, CompositeMeasurement
|
||||
from .datatypes import FlowBatteryState, ConvState, ConvOpMode
|
||||
from .datatypes import HeatCirculationPumpMode, HeatCirculationPumpState
|
||||
import syslab.config as config
|
||||
|
||||
|
||||
class SyslabUnit:
|
||||
__HIDDEN_RESOURCES = {
|
||||
'authenticate': ('boolean', 'String', 'String'),
|
||||
'isAuthenticated': ('boolean',),
|
||||
'checkConnection': ('boolean',),
|
||||
'logout': ('void',)}
|
||||
|
||||
def __init__(self, baseurl, units=None, which=None, host=None, port=None, unit_name=None, unit_type=""):
|
||||
"""
|
||||
Initialize a proxy to the given unit.
|
||||
|
||||
:param baseurl: Example: 'http://{host}:{port}/typebased_WebService_Battery/VRBBatteryWebService/{unit_name}/'
|
||||
:param units: dictionary of units to be loaded in the format {'which': ('host', 'port', 'unit_name')}
|
||||
:param which: string indicating which unit we pick from _units_
|
||||
:param host: (optional) override host
|
||||
:param port: (optional) override port
|
||||
:param unit_name: (optional) override unit_name
|
||||
:param unit_type: (optional) Used to indicate the type of the unit.
|
||||
"""
|
||||
|
||||
if units is None:
|
||||
units = {}
|
||||
|
||||
if which is not None:
|
||||
if which not in units:
|
||||
raise TypeError(
|
||||
'The {unit_type} should be one of: "{list_of_units}"'.format(
|
||||
unit_type=unit_type,
|
||||
list_of_units='", "'.join(units.keys())
|
||||
))
|
||||
else:
|
||||
host, port, unit_name = units.get(which)
|
||||
if host is not None:
|
||||
host = host
|
||||
if port is not None:
|
||||
port = port
|
||||
|
||||
assert host is not None, "When assigning custom port, host must not be None."
|
||||
assert port is not None, "When assigning custom host, port must not be None."
|
||||
assert unit_name is not None, "When assigning custom host and port, unit_name must not be None."
|
||||
|
||||
url = baseurl.format(host=host, port=port, unit_name=unit_name)
|
||||
print(url)
|
||||
self._url = url
|
||||
self._resources = parse_resources(url)
|
||||
self._resources = {**self._resources, **SyslabUnit.__HIDDEN_RESOURCES}
|
||||
# TODO: Do type checking on these types. Ignore for now
|
||||
self._complex_arg_types = ['BattOpMode', 'CompositeMeasurement', 'HeatCirculationPumpMode']
|
||||
|
||||
|
||||
if config.REMOTE_LOGGER:
|
||||
#import logger
|
||||
if config.DEBUG:
|
||||
print(f"Setting up remote logger with default IP: {config.REMOTE_IP} and port: {config.REMOTE_PORT}")
|
||||
from uuid import uuid1, getnode
|
||||
from ..comm.LogUtils import setup_udp_logger
|
||||
self.__logger = setup_udp_logger(config.SPLOG_NAME)
|
||||
self.__session_id = uuid1().__str__()
|
||||
self.__host_id = getnode().__str__()
|
||||
|
||||
# TODO: Set up local setpoints Logger
|
||||
if config.LOCAL_LOGGER:
|
||||
pass
|
||||
|
||||
def __request_int(self, resource, method, args, body) -> int:
|
||||
return int(send_request(self._url, resource, method, args, body))
|
||||
|
||||
def __request_float(self, resource, method, args, body) -> float:
|
||||
return float(send_request(self._url, resource, method, args, body))
|
||||
|
||||
def __request_boolean(self, resource, method, args, body) -> bool:
|
||||
return bool(send_request(self._url, resource, method, args, body))
|
||||
|
||||
def __request_string(self, resource, method, args, body) -> str:
|
||||
return send_request(self._url, resource, method, args, body)
|
||||
|
||||
def __request_void(self, resource, method, args, body) -> None:
|
||||
send_request(self._url, resource, method, args, body)
|
||||
|
||||
def __request_composite_boolean(self, resource, method, args, body) -> CompositeBoolean:
|
||||
|
||||
if self.__get_resource_return_type(resource) == 'CompositeBoolean':
|
||||
json_string = self.__request_string(resource, method, args, body)
|
||||
result = CompositeBoolean.parseFromJSON(json_string)
|
||||
else:
|
||||
raise TypeError('Error: resource "{0}" does not return a CompositeBoolean.'.format(resource))
|
||||
|
||||
return result
|
||||
|
||||
def __request_composite_measurement(self, resource, method, args, body) -> CompositeMeasurement:
|
||||
|
||||
if self.__get_resource_return_type(resource) == 'CompositeMeasurement':
|
||||
json_string = self.__request_string(resource, method, args, body)
|
||||
result = CompositeMeasurement.parseFromJSON(json_string)
|
||||
else:
|
||||
raise TypeError('Error: resource "{0}" does not return a CompositeMeasurement.'.format(resource))
|
||||
|
||||
return result
|
||||
|
||||
def _request_resource(self, resource, args=(), method='get', body=None, check_types=True):
|
||||
if not type(resource) is str:
|
||||
raise TypeError("Resource should be a string, found {0}.".format(type(resource)))
|
||||
|
||||
if resource not in self._resources:
|
||||
# print(resource)
|
||||
# FIXME: Workaround for metmast, where string-based methods are suffixed by a '2' and int-based methods by a '1'
|
||||
if not resource[-1] in ('1', '2'):
|
||||
raise ValueError('Unknown resource: {0} - no such resource found for {1}'.format(resource, self._url))
|
||||
|
||||
if type(args) is int or type(args) is float or type(args) is str:
|
||||
args = (args,)
|
||||
|
||||
if config.DEBUG:
|
||||
print("Resource: ", resource, " and args: ", args, "with method: ", method)
|
||||
|
||||
if config.REMOTE_LOGGER and method == 'put':
|
||||
logstr = f'SessionID:{self.__session_id}||SenderID:{self.__host_id}||Unit-URL:{self._url}||Setterfcn:{resource}||ArgValue:{args}'
|
||||
self.__logger.info(logstr)
|
||||
|
||||
return_type = self.__get_resource_return_type(resource)
|
||||
self.__check_argument(resource, args)
|
||||
result = None
|
||||
|
||||
if check_types:
|
||||
try:
|
||||
if return_type == 'CompositeMeasurement':
|
||||
result = self.__request_composite_measurement(resource, method, args, body)
|
||||
elif return_type == 'CompositeBoolean':
|
||||
result = self.__request_composite_boolean(resource, method, args, body)
|
||||
elif return_type == 'String':
|
||||
result = self.__request_string(resource, method, args, body)
|
||||
elif return_type == 'int':
|
||||
result = self.__request_int(resource, method, args, body)
|
||||
elif return_type == 'boolean':
|
||||
result = self.__request_boolean(resource, method, args, body)
|
||||
elif return_type == 'float' or return_type == 'double':
|
||||
result = self.__request_float(resource, method, args, body)
|
||||
elif return_type == 'void':
|
||||
self.__request_string(resource, method, args, body)
|
||||
elif return_type == 'BattOpMode':
|
||||
result = BattOpMode.parseFromJSON(self.__request_string(resource, method, args, body))
|
||||
elif return_type == 'FlowBatteryState':
|
||||
result = FlowBatteryState.parseFromJSON(self.__request_string(resource, method, args, body))
|
||||
elif return_type == 'ConvState':
|
||||
result = ConvState.parseFromJSON(self.__request_string(resource, method, args, body))
|
||||
elif return_type == 'EVSEState':
|
||||
result = EVSEState.parseFromJSON(self.__request_string(resource, method, args, body))
|
||||
elif return_type == 'ConvOpMode':
|
||||
result = ConvOpMode.parseFromJSON(self.__request_string(resource, method, args, body))
|
||||
elif return_type == 'HeatCirculationPumpMode':
|
||||
result = HeatCirculationPumpMode.parseFromJSON(self.__request_string(resource, method, args, body))
|
||||
elif return_type == 'HeatCirculationPumpState':
|
||||
result = HeatCirculationPumpState.parseFromJSON(self.__request_string(resource, method, args, body))
|
||||
elif return_type == 'WYEV' or return_type == 'WYEA' or return_type == 'DELV':
|
||||
import json
|
||||
result = json.loads(self.__request_string(resource, method, args, body))
|
||||
else:
|
||||
raise TypeError(
|
||||
'TypeError: The return type {0} by {1} is unknown or not supported yet.'.format(return_type,
|
||||
resource))
|
||||
except KeyError as e:
|
||||
# raise e
|
||||
raise ValueError('{0} - no such resource found for {1}'.format(resource, self._url))
|
||||
else:
|
||||
import json
|
||||
return_string = self.__request_string(resource, method, args, body)
|
||||
try:
|
||||
result = json.loads(return_string)
|
||||
except RecursionError:
|
||||
raise RecursionError(
|
||||
"Maximum recursion depth exceeded while decoding "
|
||||
"a JSON array from a unicode string. Length: {0}".format(
|
||||
len(return_string)))
|
||||
|
||||
return result
|
||||
|
||||
def __get_resource_return_type(self, resource):
|
||||
if not resource[-1] in ('1', '2'):
|
||||
# FIXME: Workaround for metmast, where string-based methods are suffixed by a '2' and int-based methods by a '1'
|
||||
return self._resources[resource][0]
|
||||
else:
|
||||
return self._resources[resource[:-1]][0]
|
||||
|
||||
def __get_resource_argument_types(self, resource):
|
||||
if not resource[-1] in ('1', '2'):
|
||||
# FIXME: Workaround for metmast, where string-based methods are suffixed by a '2' and int-based methods by a '1'
|
||||
return self._resources[resource][1:]
|
||||
else:
|
||||
return self._resources[resource[:-1]][1:]
|
||||
|
||||
def __check_argument(self, resource, args):
|
||||
arg_types = self.__get_resource_argument_types(resource)
|
||||
try:
|
||||
# TODO Ignore complex arguments (for now)
|
||||
for complex_arg_type in self._complex_arg_types:
|
||||
if complex_arg_type in arg_types:
|
||||
return
|
||||
|
||||
if len(args) == len(arg_types):
|
||||
for arg, arg_type in zip(args, arg_types):
|
||||
|
||||
if arg_type == 'String' and type(arg) is not str:
|
||||
raise TypeError()
|
||||
elif arg_type == 'int' and type(arg) is not int:
|
||||
raise TypeError()
|
||||
elif (arg_type == 'float' or arg_type == 'double') and type(arg) is not float and type(
|
||||
arg) is not int:
|
||||
raise TypeError()
|
||||
else:
|
||||
raise TypeError()
|
||||
|
||||
except TypeError:
|
||||
raise TypeError(
|
||||
'The resource "{0}" requires exactly {1} argument(s) of type {2}.'.format(resource, len(arg_types),
|
||||
arg_types))
|
||||
|
||||
|
||||
def parse_resources(url):
|
||||
import re
|
||||
from ast import literal_eval
|
||||
result = {}
|
||||
resources = send_request(url, 'resourceNames', 'get')
|
||||
resources = literal_eval(resources)
|
||||
|
||||
for resource in resources:
|
||||
try:
|
||||
# TODO - handle java arrays, ie, []
|
||||
m = re.match(r"(\w+)\[?\]? (\w+)(\([\w ,]*\))?", resource).groups()
|
||||
except AttributeError as e:
|
||||
print(e)
|
||||
continue
|
||||
args = ''
|
||||
|
||||
if m[2] is not None:
|
||||
args = tuple(m[2].replace(' ', '').replace('(', '').replace(')', '').split(','))
|
||||
result[m[1]] = (m[0],) + args
|
||||
else:
|
||||
result[m[1]] = (m[0],)
|
||||
if config.DEBUG:
|
||||
print(f'Key: {m[1]} - {m[0]} -- {args}')
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def send_request(url, resource, method, args=None, body=None):
|
||||
from requests import request
|
||||
from requests.exceptions import ConnectionError, RequestException
|
||||
|
||||
if not type(url) is str:
|
||||
raise TypeError('URL should be a string, found {0}'.format(type(url)))
|
||||
if not type(resource) is str:
|
||||
raise TypeError('URL should be a string, found {0}'.format(type(resource)))
|
||||
|
||||
kwargs = {}
|
||||
kwargs.setdefault('allow_redirects', True)
|
||||
kwargs.setdefault('headers', {'accept': 'application/json', 'content-type': 'application/json'})
|
||||
|
||||
if not url.endswith('/'):
|
||||
url += '/'
|
||||
|
||||
full_url = url + resource + '/'
|
||||
|
||||
if args is not None and len(args) > 0:
|
||||
full_url += '/'.join(tuple(str(x) for x in args))
|
||||
|
||||
if config.DEBUG:
|
||||
print(f'Calling: {full_url}')
|
||||
|
||||
try:
|
||||
response = request(method, full_url, data=body, **kwargs)
|
||||
except ConnectionError as e:
|
||||
print('Unable to connect to host: {0}.'.format(url))
|
||||
raise e
|
||||
except RequestException as e:
|
||||
print('Request error from host: {0}.'.format(full_url))
|
||||
raise e
|
||||
|
||||
if 200 <= response.status_code < 300:
|
||||
result = response.text
|
||||
elif response.status_code == 404:
|
||||
raise ConnectionError(
|
||||
'Resource not found on host or arguments are not formatted correctly: {0}'.format(response.text))
|
||||
elif response.status_code == 405:
|
||||
raise ConnectionError('Method not allowed on host: \n {0}'.format(response.text))
|
||||
elif response.status_code == 500:
|
||||
from pprint import pprint
|
||||
# TODO: Handle exception
|
||||
print('Exception on server:')
|
||||
pprint(response.json())
|
||||
raise
|
||||
else:
|
||||
raise ConnectionError(
|
||||
'Unable to successfully connect to host. Returned with HTTP status code {0}.\n Content: {1}'.format(
|
||||
response.status_code, response.text))
|
||||
|
||||
if config.DEBUG:
|
||||
print(f'Succesfully called {full_url}')
|
||||
print(f'Returned: {result}')
|
||||
|
||||
return result
|
||||
|
|
@ -0,0 +1 @@
|
|||
from . import *
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
|
||||
class BattOpMode:
|
||||
|
||||
_OPMODES = {
|
||||
0: 'BATT_UNKNOWN',
|
||||
1: 'BATT_AUTO',
|
||||
2: 'BATT_AUTO_SUBMODE1',
|
||||
3: 'BATT_AUTO_SUBMODE2',
|
||||
4: 'BATT_AUTO_SUBMODE3',
|
||||
5: 'BATT_AUTO_SUBMODE4',
|
||||
6: 'BATT_MANUAL',
|
||||
7: 'BATT_OFF',
|
||||
8: 'BATT_NOBMS',
|
||||
9: 'BATT_WITHBMS', }
|
||||
|
||||
def __init__(self, mode):
|
||||
assert mode in BattOpMode.__OPMODES
|
||||
self._mode = mode
|
||||
|
||||
@property
|
||||
def mode(self):
|
||||
return self._mode
|
||||
|
||||
def modeAsString(self):
|
||||
return BattOpMode.__OPMODES[self.mode]
|
||||
|
||||
def __str__(self):
|
||||
return 'BattOpMode: {0} ({1})'.format(self.mode, BattOpMode.__OPMODES[self.mode])
|
||||
|
||||
def parseToJSON(self):
|
||||
from json import dumps
|
||||
jsonObj = {};
|
||||
jsonObj['mode'] = self.mode;
|
||||
return dumps(jsonObj)
|
||||
|
||||
@staticmethod
|
||||
def parseFromJSON(jsonString):
|
||||
from json import JSONDecodeError, loads
|
||||
if not type(jsonString) is str: raise TypeError('jsonString should be a string, found {0}'.format(type(jsonString)))
|
||||
# result = None ## Appears unused
|
||||
|
||||
try:
|
||||
jsonObj = loads(jsonString)
|
||||
return BattOpMode(jsonObj.get('mode'))
|
||||
except JSONDecodeError as e:
|
||||
print('Not a valid JSON string: {0}.'.format(jsonString))
|
||||
raise e
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
|
||||
|
||||
# TODO: Implement
|
||||
class CommonDeviceConfig:
|
||||
pass
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
class CompositeBoolean:
|
||||
def __init__(self, value, timestampMicros, timePrecision=0, quality=0, validity=0, source=0):
|
||||
self._value = value
|
||||
self._timestampMicros = timestampMicros
|
||||
self._timePrecision = timePrecision if timePrecision != None else 0
|
||||
self._quality = quality if quality != None else 0
|
||||
self._validity = validity if validity != None else 0
|
||||
self._source = source if source != None else 0
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
return self._value
|
||||
|
||||
@property
|
||||
def timestampMicros(self):
|
||||
return self._timestampMicros
|
||||
|
||||
@property
|
||||
def timePrecision(self):
|
||||
return self._timePrecision
|
||||
|
||||
@property
|
||||
def quality(self):
|
||||
return self._quality
|
||||
|
||||
@property
|
||||
def validity(self):
|
||||
return self._validity
|
||||
|
||||
@property
|
||||
def source(self):
|
||||
return self._source
|
||||
|
||||
def __repr__(self):
|
||||
return "{0}(".format(self.__class__.__name__) + \
|
||||
"{0!r}, {1!r}, ".format(self._value, self._timestampMicros) + \
|
||||
"{0!r}, {1!r}, ".format(self._timePrecision, self._quality) + \
|
||||
"{0!r}, {1!r})".format(self._validity, self._source)
|
||||
|
||||
def __str__(self):
|
||||
from datetime import datetime
|
||||
return 'CompositeBoolean: {0} (@time: {1} UTC) '.format(self.value, datetime.utcfromtimestamp(self.timestampMicros/1000000))
|
||||
|
||||
def parseToJSON(self):
|
||||
from json import dumps
|
||||
jsonObj = {};
|
||||
jsonObj['value'] = self.value;
|
||||
jsonObj['timestampMicros'] = self.timestampMicros;
|
||||
jsonObj['timePrecision'] = self.timePrecision
|
||||
jsonObj['quality'] = self.quality
|
||||
jsonObj['validity'] = self.validity
|
||||
jsonObj['source'] = self.source;
|
||||
return dumps(jsonObj)
|
||||
|
||||
@staticmethod
|
||||
def parseFromJSON(jsonString):
|
||||
from json import JSONDecodeError, loads
|
||||
|
||||
if not type(jsonString) is str: raise TypeError('jsonString should be a string, found {0}'.format(type(jsonString)))
|
||||
try:
|
||||
jsonObj = loads(jsonString)
|
||||
result = CompositeBoolean(jsonObj.get('value'), jsonObj.get('timestampMicros'),
|
||||
jsonObj.get('timePrecision'),
|
||||
jsonObj.get('quality'), jsonObj.get('validity'), jsonObj.get('source'))
|
||||
return result
|
||||
except JSONDecodeError as e:
|
||||
print('Not a valid JSON string: {0}.'.format(jsonString))
|
||||
raise e
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
MICROS_PER_SECOND = 1000000
|
||||
|
||||
def none_to_zero(x):
|
||||
if x is None:
|
||||
return 0
|
||||
return x
|
||||
|
||||
class CompositeMeasurement:
|
||||
def __init__(self, value, timestampMicros, timePrecision=0, quality=0, validity=0, source=0):
|
||||
self._value = value
|
||||
self._timestampMicros = timestampMicros
|
||||
self._timePrecision = none_to_zero(timePrecision)
|
||||
self._quality = quality if quality != None else 0
|
||||
self._validity = validity if validity != None else 0
|
||||
self._source = source if source != None else 0
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
return self._value
|
||||
|
||||
@property
|
||||
def timestampMicros(self):
|
||||
return self._timestampMicros
|
||||
|
||||
@property
|
||||
def timePrecision(self):
|
||||
return self._timePrecision
|
||||
|
||||
@property
|
||||
def quality(self):
|
||||
return self._quality
|
||||
|
||||
@property
|
||||
def validity(self):
|
||||
return self._validity
|
||||
|
||||
@property
|
||||
def source(self):
|
||||
return self._source
|
||||
|
||||
def __str__(self):
|
||||
from datetime import datetime
|
||||
return 'CompositeMeasurement: {0} (@time: {1})'.format(self.value, datetime.fromtimestamp(self.timestampMicros/MICROS_PER_SECOND))
|
||||
|
||||
def __repr__(self):
|
||||
return "{0}(".format(self.__class__.__name__) + \
|
||||
"{0!r}, {1!r}, ".format(self._value, self._timestampMicros) + \
|
||||
"{0!r}, {1!r}, ".format(self._timePrecision, self._quality) + \
|
||||
"{0!r}, {1!r})".format(self._validity, self._source)
|
||||
|
||||
def parseToJSON(self):
|
||||
from json import dumps
|
||||
jsonObj = {};
|
||||
jsonObj['value'] = self.value;
|
||||
jsonObj['timestampMicros'] = self.timestampMicros;
|
||||
jsonObj['timePrecision'] = self.timePrecision
|
||||
jsonObj['quality'] = self.quality
|
||||
jsonObj['validity'] = self.validity
|
||||
jsonObj['source'] = self.source;
|
||||
return dumps(jsonObj)
|
||||
|
||||
@staticmethod
|
||||
def parseFromJSON(jsonString):
|
||||
from json import JSONDecodeError, loads
|
||||
|
||||
if not type(jsonString) is str: raise TypeError('jsonString should be a string, found {0}'.format(type(jsonString)))
|
||||
try:
|
||||
jsonObj = loads(jsonString)
|
||||
result = CompositeMeasurement(jsonObj.get('value'), jsonObj.get('timestampMicros'),
|
||||
jsonObj.get('timePrecision'),
|
||||
jsonObj.get('quality'), jsonObj.get('validity'), jsonObj.get('source'))
|
||||
return result
|
||||
except JSONDecodeError as e:
|
||||
print('Not a valid JSON string: {0}.'.format(jsonString))
|
||||
raise e
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
# TODO: Add Processing here
|
||||
|
||||
|
||||
class CompositeStatus:
|
||||
pass
|
||||
|
|
@ -0,0 +1,132 @@
|
|||
from .CompositeStatus import CompositeStatus
|
||||
|
||||
class ConvOpMode:
|
||||
|
||||
_OPMODES = {
|
||||
0: 'CONV_UNKNOWN',
|
||||
1: 'CONV_PQ',
|
||||
2: 'CONV_UF',
|
||||
3: 'CONV_OFF',
|
||||
}
|
||||
|
||||
def __init__(self, status, timestampMicros, timePrecision=0, quality=0, validity=0, source=0):
|
||||
assert status in ConvOpMode._OPMODES
|
||||
self._status = status
|
||||
self._timestampMicros = timestampMicros
|
||||
self._timePrecision = timePrecision
|
||||
self._quality = quality
|
||||
self._validity = validity
|
||||
self._source = source
|
||||
|
||||
@property
|
||||
def mode(self) -> int:
|
||||
return self._status
|
||||
|
||||
def modeAsString(self):
|
||||
return ConvOpMode._OPMODES[self.mode]
|
||||
|
||||
def __str__(self):
|
||||
return f'ConvOpMode({self.mode}; {ConvOpMode._OPMODES[self.mode]})'
|
||||
|
||||
def parseToJSON(self):
|
||||
from json import dumps
|
||||
jsonObj = {'mode': self.mode}
|
||||
return dumps(jsonObj)
|
||||
|
||||
@staticmethod
|
||||
def parseFromJSON(jsonString):
|
||||
from json import JSONDecodeError, loads
|
||||
if not type(jsonString) is str:
|
||||
raise TypeError('jsonString should be a string, found {0}'.format(type(jsonString)))
|
||||
try:
|
||||
jsonObj = loads(jsonString)
|
||||
return ConvOpMode(**jsonObj.get('mode'))
|
||||
except JSONDecodeError as e:
|
||||
print('Not a valid JSON string: {0}.'.format(jsonString))
|
||||
raise e
|
||||
|
||||
|
||||
class ConvState:
|
||||
|
||||
_STATES = {
|
||||
0: 'CONV_UNKNOWN',
|
||||
1: 'CONV_STOPPED',
|
||||
2: 'CONV_STARTING',
|
||||
4: 'CONV_RUNNING',
|
||||
8: 'CONV_STOPPING',
|
||||
16: 'CONV_SYNCED',
|
||||
32: 'CONV_DROOP',
|
||||
64: 'CONV_LOADENABLED',
|
||||
128: 'CONV_INHIBITED',
|
||||
1024: 'CONV_READY',
|
||||
2048: 'CONV_WARNING',
|
||||
4096: 'CONV_ALARM'}
|
||||
|
||||
def __init__(self, status, timestampMicros, timePrecision=0, quality=0, validity=0, source=0):
|
||||
self._status = status
|
||||
self._timestampMicros = timestampMicros
|
||||
self._timePrecision = timePrecision
|
||||
self._quality = quality
|
||||
self._validity = validity
|
||||
self._source = source
|
||||
|
||||
@property
|
||||
def status(self):
|
||||
return self._status
|
||||
|
||||
@property
|
||||
def timestampMicros(self):
|
||||
return self._timestampMicros
|
||||
|
||||
@property
|
||||
def timePrecision(self):
|
||||
return self._timePrecision
|
||||
|
||||
@property
|
||||
def quality(self):
|
||||
return self._quality
|
||||
|
||||
@property
|
||||
def validity(self):
|
||||
return self._validity
|
||||
|
||||
@property
|
||||
def source(self):
|
||||
return self._source
|
||||
|
||||
def statusAsString(self):
|
||||
return ConvState._STATES[self.status]
|
||||
|
||||
def __repr__(self):
|
||||
return "{0}(".format(self.__class__.__name__) + \
|
||||
"{0!r}, {1!r}, ".format(self._status, self._timestampMicros) + \
|
||||
"{0!r}, {1!r}, ".format(self._timePrecision, self._quality) + \
|
||||
"{0!r}, {1!r})".format(self._validity, self._source)
|
||||
|
||||
def __str__(self):
|
||||
from datetime import datetime
|
||||
return 'FlowBatteryState: {0} : {1} (@time: {2}))'.format(
|
||||
self._status,
|
||||
# status & statuscode evaluates to True if statuscode is 2^k and in the binary expansion of status
|
||||
";".join([label for statuscode, label in ConvState._STATES.items() if self._status & statuscode]),
|
||||
datetime.fromtimestamp(self.timestampMicros / 1000000))
|
||||
|
||||
def parseToJSON(self):
|
||||
from json import dumps
|
||||
json_obj = {'status': self._status}
|
||||
return dumps(json_obj)
|
||||
|
||||
@staticmethod
|
||||
def parseFromJSON(jsonString):
|
||||
from json import JSONDecodeError, loads
|
||||
if not type(jsonString) is str: raise TypeError('jsonString should be a string, found {0}'.format(type(jsonString)))
|
||||
# result = None
|
||||
try:
|
||||
jsonObj = loads(jsonString).get('status')
|
||||
return ConvState(
|
||||
jsonObj.get('status'), jsonObj.get('timestampMicros'),
|
||||
jsonObj.get('timePrecision'),
|
||||
jsonObj.get('quality'), jsonObj.get('validity'), jsonObj.get('source'))
|
||||
except JSONDecodeError as e:
|
||||
print('Not a valid JSON string in parsing Converter Status: {0}.'.format(jsonString))
|
||||
raise e
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
|
||||
class EVSEState:
|
||||
|
||||
_STATES = {
|
||||
0: 'EVSE_UNKNOWN',
|
||||
1: 'EVSE_NO_EV',
|
||||
2: 'EVSE_EV_STOPPED',
|
||||
4: 'EVSE_EV_READY',
|
||||
8: 'EVSE_EV_STARTING',
|
||||
16: 'EVSE_EV_CHARGING',
|
||||
32: 'EVSE_EV_DISCHARGING',
|
||||
64: 'EVSE_EV_PAUSED',
|
||||
128: 'EVSE_EV_ALARM',
|
||||
256: 'EVSE_EV_ESD', }
|
||||
|
||||
def __init__(self, status, timestampMicros, timePrecision=0, quality=0, validity=0, source=0):
|
||||
self._status = status
|
||||
self._timestampMicros = timestampMicros
|
||||
self._timePrecision = timePrecision
|
||||
self._quality = quality
|
||||
self._validity = validity
|
||||
self._source = source
|
||||
|
||||
@property
|
||||
def status(self):
|
||||
return self._status
|
||||
|
||||
@property
|
||||
def timestampMicros(self):
|
||||
return self._timestampMicros
|
||||
|
||||
@property
|
||||
def timePrecision(self):
|
||||
return self._timePrecision
|
||||
|
||||
@property
|
||||
def quality(self):
|
||||
return self._quality
|
||||
|
||||
@property
|
||||
def validity(self):
|
||||
return self._validity
|
||||
|
||||
@property
|
||||
def source(self):
|
||||
return self._source
|
||||
|
||||
def statusAsString(self):
|
||||
return EVSEState.__STATES[self.status]
|
||||
|
||||
def __repr__(self):
|
||||
return "{0}(".format(self.__class__.__name__) + \
|
||||
"{0!r}, {1!r}, ".format(self._status, self._timestampMicros) + \
|
||||
"{0!r}, {1!r}, ".format(self._timePrecision, self._quality) + \
|
||||
"{0!r}, {1!r})".format(self._validity, self._source)
|
||||
|
||||
def __str__(self):
|
||||
from datetime import datetime
|
||||
return 'EVSEState: {0} : {1} (@time: {2}))'.format(
|
||||
self.status,
|
||||
# status & statuscode evaluates to True if statuscode is 2^k and in the binary expansion of status
|
||||
";".join([label for statuscode, label in EVSEState._STATES.items() if self._status & statuscode]),
|
||||
datetime.fromtimestamp(self.timestampMicros / 1000000))
|
||||
|
||||
def parseToJSON(self):
|
||||
from json import dumps
|
||||
jsonObj = {'status': self.status}
|
||||
return dumps(jsonObj)
|
||||
|
||||
@staticmethod
|
||||
def parseFromJSON(jsonString):
|
||||
from json import JSONDecodeError, loads
|
||||
if not type(jsonString) is str: raise TypeError('jsonString should be a string, found {0}'.format(type(jsonString)))
|
||||
# result = None
|
||||
try:
|
||||
jsonObj = loads(jsonString).get('status')
|
||||
return EVSEState(
|
||||
jsonObj.get('status'), jsonObj.get('timestampMicros'),
|
||||
jsonObj.get('timePrecision'),
|
||||
jsonObj.get('quality'), jsonObj.get('validity'), jsonObj.get('source'))
|
||||
except JSONDecodeError as e:
|
||||
print('Not a valid JSON string: {0}.'.format(jsonString))
|
||||
raise e
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
|
||||
class FlowBatteryState:
|
||||
|
||||
_STATES = {
|
||||
0: 'BATT_UNKNOWN',
|
||||
1: 'BATT_STOPPED',
|
||||
2: 'BATT_STARTING',
|
||||
4: 'BATT_FLOODING',
|
||||
8: 'BATT_RUNNING',
|
||||
16: 'BATT_STOPPING',
|
||||
32: 'BATT_DRAINING',
|
||||
64: 'BATT_PUMPSRAMP',
|
||||
128: 'BATT_TANKVALVESOP',
|
||||
256: 'BATT_PUMPSRUN',
|
||||
512: 'BATT_DCBREAKER',
|
||||
1024: 'PCS_READY',
|
||||
2048: 'PCS_RUNNING',
|
||||
4096: 'BATT_ALARM',
|
||||
8192: 'BATT_ESD'}
|
||||
|
||||
def __init__(self, status, timestampMicros, timePrecision=0, quality=0, validity=0, source=0):
|
||||
self._status = status
|
||||
self._timestampMicros = timestampMicros
|
||||
self._timePrecision = timePrecision
|
||||
self._quality = quality
|
||||
self._validity = validity
|
||||
self._source = source
|
||||
|
||||
@property
|
||||
def status(self):
|
||||
return self._status
|
||||
|
||||
@property
|
||||
def timestampMicros(self):
|
||||
return self._timestampMicros
|
||||
|
||||
@property
|
||||
def timePrecision(self):
|
||||
return self._timePrecision
|
||||
|
||||
@property
|
||||
def quality(self):
|
||||
return self._quality
|
||||
|
||||
@property
|
||||
def validity(self):
|
||||
return self._validity
|
||||
|
||||
@property
|
||||
def source(self):
|
||||
return self._source
|
||||
|
||||
def statusAsString(self):
|
||||
return FlowBatteryState.__STATES[self.status]
|
||||
|
||||
def __repr__(self):
|
||||
return "{0}(".format(self.__class__.__name__) + \
|
||||
"{0!r}, {1!r}, ".format(self._status, self._timestampMicros) + \
|
||||
"{0!r}, {1!r}, ".format(self._timePrecision, self._quality) + \
|
||||
"{0!r}, {1!r})".format(self._validity, self._source)
|
||||
|
||||
def __str__(self):
|
||||
from datetime import datetime
|
||||
return 'FlowBatteryState: {0} : {1} (@time: {2}))'.format(
|
||||
self.status,
|
||||
# status & statuscode evaluates to True if statuscode is 2^k and in the binary expansion of status
|
||||
";".join([label for statuscode, label in FlowBatteryState._STATES.items() if self._status & statuscode]),
|
||||
datetime.fromtimestamp(self.timestampMicros / 1000000))
|
||||
|
||||
def parseToJSON(self):
|
||||
from json import dumps
|
||||
jsonObj = {'status': self.status}
|
||||
return dumps(jsonObj)
|
||||
|
||||
@staticmethod
|
||||
def parseFromJSON(jsonString):
|
||||
from json import JSONDecodeError, loads
|
||||
if not type(jsonString) is str: raise TypeError('jsonString should be a string, found {0}'.format(type(jsonString)))
|
||||
# result = None
|
||||
try:
|
||||
jsonObj = loads(jsonString).get('status')
|
||||
return FlowBatteryState(
|
||||
jsonObj.get('status'), jsonObj.get('timestampMicros'),
|
||||
jsonObj.get('timePrecision'),
|
||||
jsonObj.get('quality'), jsonObj.get('validity'), jsonObj.get('source'))
|
||||
except JSONDecodeError as e:
|
||||
print('Not a valid JSON string: {0}.'.format(jsonString))
|
||||
raise e
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
|
||||
class HeatCirculationPumpMode:
|
||||
|
||||
__OPMODES = {
|
||||
-1: 'PUMP_MODE_UNKNOWN',
|
||||
0: 'PUMP_MODE_CONSTANT_SPEED',
|
||||
1: 'PUMP_MODE_CONSTANT_FREQUENCY',
|
||||
3: 'PUMP_MODE_CONSTANT_HEAD',
|
||||
4: 'PUMP_MODE_CONSTANT_PRESSURE',
|
||||
5: 'PUMP_MODE_CONSTANT_DIFF_PRESSURE',
|
||||
6: 'PUMP_MODE_PROPORTIONAL_PRESSURE',
|
||||
7: 'PUMP_MODE_CONSTANT_FLOW',
|
||||
8: 'PUMP_MODE_CONSTANT_TEMP',
|
||||
10: 'PUMP_MODE_CONSTANT_LEVEL',
|
||||
128: 'PUMP_MODE_AUTO_ADAPT',
|
||||
129: 'PUMP_MODE_FLOW_ADAPT' }
|
||||
|
||||
__OPMODES_R = {v: k for k, v in __OPMODES.items()}
|
||||
|
||||
def __init__(self, status, timestampMicros, timePrecision=0, quality=0, validity=0, source=0):
|
||||
"""
|
||||
@Input:
|
||||
mode (int or str): Pump operating mode, can be both int and string
|
||||
|
||||
To get list of available modes: HeatCirculationPumpMode.opmodes()
|
||||
"""
|
||||
if status in HeatCirculationPumpMode.__OPMODES:
|
||||
self._status = status
|
||||
elif mode in HeatCirculationPumpMode.__OPMODES_R:
|
||||
self._status = HeatCirculationPumpMode.__OPMODES_R[status]
|
||||
else:
|
||||
raise AssertionError('HeatCirculationPumpMode not recognized: \'{0}\''.format(mode))
|
||||
self._timestampMicros = timestampMicros
|
||||
self._timePrecision = timePrecision
|
||||
self._quality = quality
|
||||
self._validity = validity
|
||||
self._source = source
|
||||
|
||||
@staticmethod
|
||||
def opmodes():
|
||||
return HeatCirculationPumpMode.__OPMODES.copy()
|
||||
|
||||
@property
|
||||
def mode(self):
|
||||
return self._status
|
||||
|
||||
@property
|
||||
def modeAsString(self):
|
||||
return HeatCirculationPumpMode.__OPMODES[self.mode]
|
||||
|
||||
def __repr__(self):
|
||||
return "{0}(".format(self.__class__.__name__) + \
|
||||
"{0!r}, {1!r}, ".format(self._status, self._timestampMicros) + \
|
||||
"{0!r}, {1!r}, ".format(self._timePrecision, self._quality) + \
|
||||
"{0!r}, {1!r})".format(self._validity, self._source)
|
||||
def __str__(self):
|
||||
return 'HeatCirculationPumpMode: {0} ({1})'.format(self.mode, self.modeAsString)
|
||||
|
||||
def parseToJSON(self):
|
||||
from json import dumps
|
||||
jsonObj = {};
|
||||
jsonObj['mode'] = {
|
||||
'status': self._status,
|
||||
'timestampMicros': self._timestampMicros,
|
||||
'timePrecision': self._timePrecision,
|
||||
'quality': self._quality,
|
||||
'validity': self._validity,
|
||||
'source': self._source,
|
||||
}
|
||||
return dumps(jsonObj)
|
||||
|
||||
@staticmethod
|
||||
def parseFromJSON(jsonString):
|
||||
from json import JSONDecodeError, loads
|
||||
if not type(jsonString) is str: raise TypeError('jsonString should be a string, found {0}'.format(type(jsonString)))
|
||||
# result = None ## Appears unused
|
||||
|
||||
try:
|
||||
jsonObj = loads(jsonString).get('mode')
|
||||
return HeatCirculationPumpMode(
|
||||
jsonObj.get('status'),
|
||||
jsonObj.get('timestampMicros'),
|
||||
jsonObj.get('timePrecision'),
|
||||
jsonObj.get('quality'),
|
||||
jsonObj.get('validity'),
|
||||
jsonObj.get('source'))
|
||||
except JSONDecodeError as e:
|
||||
print('Not a valid JSON string: {0}.'.format(jsonString))
|
||||
raise e
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
|
||||
class HeatCirculationPumpState:
|
||||
|
||||
__OPSTATES = {
|
||||
-1: 'PUMP_STATE_UNKNOWN',
|
||||
0: 'PUMP_STATE_STOPPED',
|
||||
1: 'PUMP_STATE_RUNNING',
|
||||
2: 'PUMP_STATE_ERROR',}
|
||||
__OPSTATES_R = {v: k for k, v in __OPSTATES.items()}
|
||||
|
||||
def __init__(self, status, timestampMicros, timePrecision=0, quality=0, validity=0, source=0):
|
||||
"""
|
||||
@Input:
|
||||
status (int or str): Pump operating mode, can be both int and string
|
||||
|
||||
To get list of available modes: HeatCirculationPumpState.opstates()
|
||||
"""
|
||||
if status in HeatCirculationPumpState.__OPSTATES:
|
||||
self._status = status
|
||||
elif state in HeatCirculationPumpState.__OPSTATES_R:
|
||||
self._status = HeatCirculationPumpState.__OPSTATES_R[status]
|
||||
else:
|
||||
raise AssertionError('HeatCirculationPumpState not recognized: \'{0}\''.format(mode))
|
||||
self._timestampMicros = timestampMicros
|
||||
self._timePrecision = timePrecision
|
||||
self._quality = quality
|
||||
self._validity = validity
|
||||
self._source = source
|
||||
|
||||
|
||||
@staticmethod
|
||||
def opstates():
|
||||
return HeatCirculationPumpState.__OPSTATES.copy()
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
return self._status
|
||||
|
||||
@property
|
||||
def stateAsString(self):
|
||||
return HeatCirculationPumpState.__OPSTATES[self._status]
|
||||
|
||||
def __repr__(self):
|
||||
return "{0}(".format(self.__class__.__name__) + \
|
||||
"{0!r}, {1!r}, ".format(self._status, self._timestampMicros) + \
|
||||
"{0!r}, {1!r}, ".format(self._timePrecision, self._quality) + \
|
||||
"{0!r}, {1!r})".format(self._validity, self._source)
|
||||
|
||||
|
||||
def __str__(self):
|
||||
return 'HeatCirculationPumpState: {0} ({1})'.format(self.state, self.stateAsString)
|
||||
|
||||
def parseToJSON(self):
|
||||
from json import dumps
|
||||
jsonObj = {}
|
||||
jsonObj['state'] = {
|
||||
'status': self._status,
|
||||
'timestampMicros': self._timestampMicros,
|
||||
'timePrecision': self._timePrecision,
|
||||
'quality': self._quality,
|
||||
'validity': self._validity,
|
||||
'source': self._source,
|
||||
}
|
||||
return dumps(jsonObj)
|
||||
|
||||
@staticmethod
|
||||
def parseFromJSON(jsonString):
|
||||
from json import JSONDecodeError, loads
|
||||
if not type(jsonString) is str: raise TypeError('jsonString should be a string, found {0}'.format(type(jsonString)))
|
||||
# result = None ## Appears unused
|
||||
|
||||
try:
|
||||
jsonObj = loads(jsonString).get('state')
|
||||
return HeatCirculationPumpState(
|
||||
jsonObj.get('status'),
|
||||
jsonObj.get('timestampMicros'),
|
||||
jsonObj.get('timePrecision'),
|
||||
jsonObj.get('quality'),
|
||||
jsonObj.get('validity'),
|
||||
jsonObj.get('source'))
|
||||
except JSONDecodeError as e:
|
||||
print('Not a valid JSON string: {0}.'.format(jsonString))
|
||||
raise e
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
from dataclasses import dataclass
|
||||
from . import CompositeMeasurement
|
||||
from typing import Optional
|
||||
# TODO: Add processing to these.
|
||||
|
||||
|
||||
@dataclass
|
||||
class DELV:
|
||||
phaseAB: CompositeMeasurement
|
||||
phaseBC: CompositeMeasurement
|
||||
phaseCA: CompositeMeasurement
|
||||
phaseAverage: CompositeMeasurement
|
||||
|
||||
@staticmethod
|
||||
def parseFromJSON(json_string):
|
||||
from json import JSONDecodeError, loads
|
||||
if not type(json_string) is str:
|
||||
raise TypeError('jsonString should be a string, found {0}'.format(type(json_string)))
|
||||
|
||||
try:
|
||||
jsonObj = loads(json_string)
|
||||
phaseABcm = CompositeMeasurement(**jsonObj.get('phaseAB'))
|
||||
phaseBCcm = CompositeMeasurement(**jsonObj.get('phaseBC'))
|
||||
phaseCAcm = CompositeMeasurement(**jsonObj.get('phaseCA'))
|
||||
phaseAVGcm = CompositeMeasurement(**jsonObj.get('phaseAverage'))
|
||||
return DELV(phaseABcm, phaseBCcm, phaseCAcm, phaseAVGcm)
|
||||
except JSONDecodeError as e:
|
||||
print('Not a valid JSON string: {0}.'.format(json_string))
|
||||
raise e
|
||||
|
||||
|
||||
class GPSL:
|
||||
pass
|
||||
|
||||
|
||||
class HLTH:
|
||||
pass
|
||||
|
||||
|
||||
class LNPL:
|
||||
pass
|
||||
|
||||
|
||||
class PNPL:
|
||||
pass
|
||||
|
||||
|
||||
@dataclass()
|
||||
class WYEA:
|
||||
phaseAB: CompositeMeasurement
|
||||
phaseBC: CompositeMeasurement
|
||||
phaseCA: CompositeMeasurement
|
||||
neutral: Optional[CompositeMeasurement] = None
|
||||
|
||||
@staticmethod
|
||||
def parseFromJSON(json_string):
|
||||
from json import JSONDecodeError, loads
|
||||
if not type(json_string) is str:
|
||||
raise TypeError('jsonString should be a string, found {0}'.format(type(json_string)))
|
||||
|
||||
try:
|
||||
jsonObj = loads(json_string)
|
||||
phaseAcm = CompositeMeasurement(**jsonObj.get('phaseA'))
|
||||
phaseBcm = CompositeMeasurement(**jsonObj.get('phaseB'))
|
||||
phaseCcm = CompositeMeasurement(**jsonObj.get('phaseC'))
|
||||
neutral = jsonObj.get('neutral')
|
||||
if neutral is not None:
|
||||
neutral = CompositeMeasurement(**neutral)
|
||||
return WYEA(phaseAcm, phaseBcm, phaseCcm, neutral)
|
||||
|
||||
except JSONDecodeError as e:
|
||||
print('Not a valid JSON string: {0}.'.format(json_string))
|
||||
raise e
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
__all__ = []
|
||||
|
||||
# Composite Data Types
|
||||
from .CompositeBoolean import CompositeBoolean
|
||||
from .CompositeMeasurement import CompositeMeasurement
|
||||
from .CompositeStatus import CompositeStatus
|
||||
|
||||
# SYSLAB-specific data types
|
||||
from .Identifiers import DELV, GPSL, HLTH, LNPL, PNPL, WYEA
|
||||
|
||||
# Unit-specific data types
|
||||
from .BattOpMode import BattOpMode
|
||||
from .FlowBatteryState import FlowBatteryState
|
||||
from .HeatCirculationPumpMode import HeatCirculationPumpMode
|
||||
from .HeatCirculationPumpState import HeatCirculationPumpState
|
||||
from .ConverterTypes import ConvState, ConvOpMode
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
def get_flexhouse(simulated=False, physical=False, simulate_on_dumpload_ID="", simulate_on_battery=False):
|
||||
"""
|
||||
Return an instantiated object which operates as a flexhouse.
|
||||
input:
|
||||
simulated (bool): Whether the flexhouse should be simulated
|
||||
physical (bool): Whether the flexhouse should be the real flexhouse
|
||||
simulate_on_dumpload_ID (string): The ID of the dumpload on which to simulate the flexhouse
|
||||
simulate_on_battery (bool): Whether to simulate on battery
|
||||
|
||||
return:
|
||||
flexhouse: A flexhouse object
|
||||
"""
|
||||
|
||||
if simulated:
|
||||
if simulate_on_battery:
|
||||
from .virtual.FlexHouse_sim_batt import FlexHouse_sim_batt
|
||||
return FlexHouse_sim_batt('batt1')
|
||||
else:
|
||||
from .virtual.FlexHouse_sim import FlexHouse_sim
|
||||
assert simulate_on_dumpload_ID != "", "Must supply an ID string for the dumpload used in Flexhouse simulation if not simulating on battery"
|
||||
return FlexHouse_sim(simulate_on_dumpload_ID)
|
||||
elif physical:
|
||||
from .physical.FlexHouse_real import FlexHouse_real
|
||||
return FlexHouse_real()
|
||||
else:
|
||||
raise Exception('Must define if FlexHouse instance is real or simulated')
|
||||
|
||||
|
|
@ -0,0 +1,258 @@
|
|||
from ..core.SyslabUnit import SyslabUnit
|
||||
from ..core.datatypes import CompositeMeasurement, CompositeBoolean, CompositeStatus
|
||||
from ..core.datatypes import CommonDeviceConfig
|
||||
from ..core.datatypes import DELV, WYEA, GPSL, PNPL, LNPL, HLTH, ConvOpMode, ConvState
|
||||
from typing import List, Union
|
||||
from time import time
|
||||
|
||||
|
||||
def cast_to_cm(m: Union[CompositeMeasurement, float]):
|
||||
if type(m) == float:
|
||||
# TODO: Is there a better way to estimate precision of time.time?
|
||||
request = CompositeMeasurement(m, timestampMicros=time()*1e6, timePrecision=1000)
|
||||
elif type(m) == CompositeMeasurement:
|
||||
request = m
|
||||
else:
|
||||
raise TypeError(f"Unknown request type: {type(m)}")
|
||||
return request
|
||||
|
||||
|
||||
class B2BConverter(SyslabUnit):
|
||||
"""
|
||||
Class covering back-to-back converters in SYSLAB.
|
||||
|
||||
A full list of available switchboards can be found by calling 'SwitchBoard.getAvailableSwitchBoards()'
|
||||
|
||||
Alternatively, the user may specify a host and port to connect to via the *host* and *port* arguments.
|
||||
"""
|
||||
|
||||
_CONVERTERS = {
|
||||
'ABBB2B': ('syslab-04.syslab.dk', '8080', 'B2BConverter'),
|
||||
}
|
||||
MAXP = 60
|
||||
MAXQ = 60
|
||||
|
||||
@staticmethod
|
||||
def getAvailableUnits():
|
||||
return list(B2BConverter.__CONVERTERS.keys())
|
||||
|
||||
def __init__(self, which=None, host=None, port=None, unit_name=None):
|
||||
|
||||
baseurl = 'http://{host}:{port}/typebased_WebService_ABBConverter/ABBConverterWebService/{unit_name}/'
|
||||
|
||||
super().__init__(
|
||||
baseurl=baseurl,
|
||||
which=which,
|
||||
units=self._CONVERTERS,
|
||||
host=host,
|
||||
port=port,
|
||||
unit_name=unit_name,
|
||||
unit_type="B2BConverter")
|
||||
|
||||
# Inventory functions
|
||||
|
||||
# Configuration class used for HMI layout
|
||||
def getNodeConfiguration(self, ) -> CommonDeviceConfig:
|
||||
return self._request_resource('getNodeConfiguration')
|
||||
|
||||
# Component description
|
||||
def getConverterName(self, ) -> str:
|
||||
return self._request_resource('getConverterName')
|
||||
|
||||
def getConverterLogicalNameplate(self, ) -> LNPL:
|
||||
raise NotImplementedError
|
||||
|
||||
def getConverterPhysicalNameplate(self, ) -> PNPL:
|
||||
raise NotImplementedError
|
||||
|
||||
def getConverterHealth(self, ) -> HLTH:
|
||||
raise NotImplementedError
|
||||
|
||||
def getGPSLocation(self, ) -> GPSL:
|
||||
raise NotImplementedError
|
||||
|
||||
# Operating characteristics (corresponding to COPR/DRAT node)
|
||||
def getRatedP(self, ) -> float:
|
||||
raise NotImplementedError
|
||||
# return self._request_resource('getRatedP')
|
||||
|
||||
def getRatedS(self, ) -> float:
|
||||
raise NotImplementedError
|
||||
# return self._request_resource('getRatedS')
|
||||
|
||||
def getRatedQ(self, ) -> float:
|
||||
raise NotImplementedError
|
||||
# return self._request_resource('getRatedQ')
|
||||
|
||||
def getRatedU(self, ) -> float:
|
||||
raise NotImplementedError
|
||||
# return self._request_resource('getRatedU')
|
||||
|
||||
def getRatedI(self, ) -> float:
|
||||
raise NotImplementedError
|
||||
# return self._request_resource('getRatedI')
|
||||
|
||||
def getRatedf(self, ) -> float:
|
||||
raise NotImplementedError
|
||||
# return self._request_resource('getRatedf')
|
||||
|
||||
# Operating mode settings (corresponding to DOPM node)
|
||||
def getAvailableOperatingModes(self, ) -> List[ConvOpMode]:
|
||||
raise NotImplementedError
|
||||
|
||||
def getCurrentOperatingMode(self, ) -> ConvOpMode:
|
||||
return self._request_resource('getCurrentOperatingMode')
|
||||
|
||||
def setOperatingMode(self, mode: ConvOpMode) -> None:
|
||||
raise NotImplementedError
|
||||
|
||||
def isPSetpointEnabled(self, ) -> CompositeBoolean:
|
||||
return self._request_resource('isPSetpointEnabled')
|
||||
|
||||
def isQSetpointEnabled(self, ) -> CompositeBoolean:
|
||||
return self._request_resource('isQSetpointEnabled')
|
||||
|
||||
def isUSetpointEnabled(self, ) -> CompositeBoolean:
|
||||
return self._request_resource('isUSetpointEnabled')
|
||||
|
||||
def isfSetpointEnabled(self, ) -> CompositeBoolean:
|
||||
return self._request_resource('isfSetpointEnabled')
|
||||
|
||||
# Status information (corresponding to DPST node)
|
||||
def getConverterStatus(self, ) -> ConvState:
|
||||
return self._request_resource('getConverterStatus')
|
||||
|
||||
# Alarms information
|
||||
def hasActiveFault(self, ) -> CompositeBoolean:
|
||||
raise NotImplementedError
|
||||
|
||||
def hasActiveWarning(self, ) -> CompositeBoolean:
|
||||
raise NotImplementedError
|
||||
|
||||
def resetAlarms(self, ) -> None:
|
||||
raise NotImplementedError
|
||||
|
||||
def getActiveEventCode(self, ) -> CompositeStatus:
|
||||
raise NotImplementedError
|
||||
|
||||
# DER controller characteristics (corresponding to DRCT node)
|
||||
def setActivePower(self, m: Union[CompositeMeasurement, float]) -> None:
|
||||
"""
|
||||
Send a request for the converter's output active power. Requires the converter to be in PQ mode.
|
||||
|
||||
:param m: [kW] Requested active power. If a float, will be converted to a CompositeMeasurement with the current system time as timestamp.)
|
||||
:return: None
|
||||
"""
|
||||
|
||||
P_UpperLimit = 15.0
|
||||
P_LowerLimit = -15.0
|
||||
|
||||
return self._request_resource('setActivePower', (), 'put', cast_to_cm(max(P_LowerLimit, min(m, P_UpperLimit))).parseToJSON())
|
||||
|
||||
def setFrequency(self, m: Union[CompositeMeasurement, float]) -> None:
|
||||
"""
|
||||
Send a request for the converter's output power. Requires the converter to be in UF mode.
|
||||
|
||||
:param m: [Hz] Requested active power. If a float, will be converted to a CompositeMeasurement with the current system time as timestamp.)
|
||||
:return: None
|
||||
"""
|
||||
return self._request_resource('seFrequency', (), 'put', cast_to_cm(m).parseToJSON())
|
||||
|
||||
def setReactivePower(self, m: Union[CompositeMeasurement, float]) -> None:
|
||||
"""
|
||||
Send a request for the converter's output reactive power. Requires the converter to be in PQ mode.
|
||||
|
||||
:param m: [kVA] Requested active power. If a float, will be converted to a CompositeMeasurement with the current system time as timestamp.)
|
||||
:return: None
|
||||
"""
|
||||
|
||||
Q_UpperLimit = 15.0
|
||||
Q_LowerLimit = -15.0
|
||||
|
||||
return self._request_resource('setReactivePower', (), 'put', cast_to_cm(max(Q_LowerLimit, min(m, Q_UpperLimit))).parseToJSON())
|
||||
|
||||
def setVoltage(self, m: Union[CompositeMeasurement, float]) -> None:
|
||||
"""
|
||||
Send a request for the converter's output voltage. Requires the converter to be in UF mode.
|
||||
|
||||
:param m: [V] Requested active power. If a float, will be converted to a CompositeMeasurement with the current system time as timestamp.)
|
||||
:return: None
|
||||
"""
|
||||
return self._request_resource('setVoltage', (), 'put', cast_to_cm(m).parseToJSON())
|
||||
|
||||
def getActivePowerSetpoint(self, ) -> CompositeMeasurement:
|
||||
raise NotImplementedError
|
||||
|
||||
def getFrequencySetpoint(self, ) -> CompositeMeasurement:
|
||||
raise NotImplementedError
|
||||
|
||||
def getReactivePowerSetpoint(self, ) -> CompositeMeasurement:
|
||||
raise NotImplementedError
|
||||
|
||||
def getVoltageSetpoint(self, ) -> CompositeMeasurement:
|
||||
raise NotImplementedError
|
||||
|
||||
# Synchronisation (corresponding to RSYN node)
|
||||
def synchronize(self, ) -> None:
|
||||
raise NotImplementedError
|
||||
|
||||
def unsynchronize(self, ) -> None:
|
||||
raise NotImplementedError
|
||||
|
||||
def setDroopEnable(self, b: CompositeBoolean) -> None:
|
||||
raise NotImplementedError
|
||||
|
||||
def setLoadEnable(self, b: CompositeBoolean) -> None:
|
||||
raise NotImplementedError
|
||||
|
||||
def getVoltageDroopPct(self, ) -> CompositeMeasurement:
|
||||
raise NotImplementedError
|
||||
|
||||
def getFrequencyDroopPct(self, ) -> CompositeMeasurement:
|
||||
raise NotImplementedError
|
||||
|
||||
def setVoltageDroopPct(self, pct: CompositeMeasurement) -> None:
|
||||
raise NotImplementedError
|
||||
|
||||
def setFrequencyDroopPct(self, pct: CompositeMeasurement) -> None:
|
||||
raise NotImplementedError
|
||||
|
||||
# Reciprocating Engine (corresponds to DCIP node)
|
||||
def startConverter(self, ) -> None:
|
||||
return self._request_resource('startConverter', (), 'put')
|
||||
|
||||
def softStopConverter(self, ) -> None:
|
||||
return self._request_resource('softStopConverter', (), 'put')
|
||||
|
||||
def stopConverter(self, ) -> None:
|
||||
return self._request_resource('stopConverter', (), 'put')
|
||||
|
||||
# AC quantities (corresponds to MMXU nodes)
|
||||
def getActivePowerOutput(self, ) -> CompositeMeasurement:
|
||||
return self._request_resource('getActivePowerOutput')
|
||||
|
||||
def getReactivePowerOutput(self, ) -> CompositeMeasurement:
|
||||
return self._request_resource('getReactivePowerOutput')
|
||||
|
||||
def getOutputFrequency(self, ) -> CompositeMeasurement:
|
||||
return self._request_resource('getOutputFrequency')
|
||||
|
||||
def getOutputInterphaseVoltages(self, ) -> DELV:
|
||||
return self._request_resource('getOutputInterphaseVoltages')
|
||||
|
||||
def getOutputPhaseCurrents(self, ) -> WYEA:
|
||||
raise NotImplementedError
|
||||
|
||||
def getRectifierInterphaseVoltages(self, ) -> DELV:
|
||||
return self._request_resource('getRectifierInterphaseVoltages')
|
||||
|
||||
def getRectifierPhaseCurrents(self, ) -> WYEA:
|
||||
return self._request_resource('getRectifierPhaseCurrents')
|
||||
|
||||
def getSyncBusInterphaseVoltages(self, ) -> DELV:
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class ABBB2BConverter(B2BConverter):
|
||||
def __init__(self):
|
||||
super(ABBB2BConverter, self).__init__(which='ABBB2B')
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
from ..core.SyslabUnit import SyslabUnit
|
||||
from ..core.datatypes.FlowBatteryState import FlowBatteryState
|
||||
from ..core.datatypes.BattOpMode import BattOpMode
|
||||
|
||||
class Battery(SyslabUnit):
|
||||
"""The Battery class represents a battery in SYSLAB.
|
||||
The Battery class is instantiated using a string with the unique name of the battery, ie. 'which'
|
||||
|
||||
A full list of available batteries can be found by calling 'Battery.getAvailableBatteries()'
|
||||
|
||||
Alternatively, the user may specify a host and port to connect to via the *host* and *port* arguments.
|
||||
"""
|
||||
|
||||
__BATTERIES = {
|
||||
'batt1': ('syslab-12.syslab.dk', '8080', 'batt1'),
|
||||
'battemu': ('syslab-31.syslab.dk', '8080', 'battemu'),
|
||||
'battfh1': ('syslab-s01.syslab.dk', '8080', 'battfh1'),
|
||||
'simlab-15': ('192.168.0.115', '8080', 'batt1'),
|
||||
'vbatt1': ('simlab-12', '8080', 'batt1'),
|
||||
}
|
||||
|
||||
def __init__(self, which=None, host=None, port=None, unitname=None):
|
||||
|
||||
baseurl = 'http://{host}:{port}/typebased_WebService_Battery/VRBBatteryWebService/{unit_name}/'
|
||||
super().__init__(
|
||||
baseurl=baseurl,
|
||||
which=which,
|
||||
units=self.__BATTERIES,
|
||||
host=host,
|
||||
port=port,
|
||||
unit_name=unitname,
|
||||
unit_type="Battery")
|
||||
|
||||
def startBattery(self):
|
||||
return self._request_resource('startBattery', (), 'put')
|
||||
|
||||
def stopBattery(self):
|
||||
return self._request_resource('stopBattery', (), 'put')
|
||||
|
||||
def getActivePower(self):
|
||||
return self._request_resource('getACActivePower')
|
||||
|
||||
def setActivePower(self, setPoint):
|
||||
|
||||
P_UpperLimit = 15.0
|
||||
P_LowerLimit = -15.0
|
||||
|
||||
return self._request_resource('setP', (max(P_LowerLimit, min(setPoint, P_UpperLimit))), 'put')
|
||||
|
||||
def getReactivePower(self):
|
||||
return self._request_resource('getACReactivePower')
|
||||
|
||||
def setReactivePower(self, setPoint):
|
||||
return self._request_resource('setQ', (setPoint), 'put')
|
||||
|
||||
def getFrequency(self):
|
||||
return self._request_resource('getACFrequency')
|
||||
|
||||
def getRemainingFloodTime(self):
|
||||
return min(self._request_resource('getRemainingFloodTime'), self._request_resource('getRemainingDrainTime'))
|
||||
|
||||
def getCurrentOperatingMode(self):
|
||||
return self._request_resource('getCurrentOperatingMode')
|
||||
|
||||
def getCurrentOperatingState(self):
|
||||
return self._request_resource('getCurrentOperatingState')
|
||||
|
||||
def setOperatingMode(self, mode):
|
||||
return self._request_resource('setOperatingMode', (), 'put', BattOpMode(mode).parseToJSON())
|
||||
|
||||
def getSOC(self):
|
||||
return self._request_resource('getSOC')
|
||||
|
||||
def getRatedActivePower(self):
|
||||
return self._request_resource('getRatedP')
|
||||
|
||||
def getRatedReactivePower(self):
|
||||
return self._request_resource('getRatedQ')
|
||||
|
||||
def getName(self):
|
||||
return self._request_resource('getBatteryName')
|
||||
|
||||
@staticmethod
|
||||
def getAvailableBatteries():
|
||||
return list(Battery.__BATTERIES.keys())
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
from ..core.SyslabUnit import SyslabUnit
|
||||
|
||||
|
||||
class DieselGenerator(SyslabUnit):
|
||||
"""The DieselGenerator class represents a photovoltaic panel array in SYSLAB.
|
||||
The DieselGenerator class is instantiated using a string with the unique name of the dumpload, ie. 'which'
|
||||
|
||||
A full list of available panel arrays can be found by calling 'DieselGenerator.getAvailableDieselGenerator()'
|
||||
|
||||
Alternatively, the user may specify a host and port to connect to via the *host* and *port* arguments.
|
||||
"""
|
||||
|
||||
__UNITS = {
|
||||
'diesel319': ('syslab-02.syslab.dk', '8080', 'genset1'),
|
||||
}
|
||||
|
||||
def __init__(self, which=None, host=None, port=None, unitname=None):
|
||||
|
||||
baseurl = 'http://{host}:{port}/typebased_WebService_Diesel/DEIFDieselGensetWebService/{unit_name}/'
|
||||
super().__init__(
|
||||
baseurl=baseurl,
|
||||
which=which,
|
||||
units=self.__UNITS,
|
||||
host=host,
|
||||
port=port,
|
||||
unit_name=unitname,
|
||||
unit_type="DieselGenerator")
|
||||
|
||||
def getRatedP(self):
|
||||
return self._request_resource('getRatedP')
|
||||
|
||||
def getRatedQ(self):
|
||||
return self._request_resource('getRatedQ')
|
||||
|
||||
def getRatedS(self):
|
||||
return self._request_resource('getRatedS')
|
||||
|
||||
def getRatedU(self):
|
||||
return self._request_resource('getRatedU')
|
||||
|
||||
def getRatedf(self):
|
||||
return self._request_resource('getRatedf')
|
||||
|
||||
def isGeneratorBreakerClosed(self):
|
||||
return self._request_resource('isGeneratorBreakerClosed')
|
||||
|
||||
def isGensetRunning(self):
|
||||
return self._request_resource('isGensetRunning')
|
||||
|
||||
def isGensetSynchronized(self):
|
||||
return self._request_resource('isGensetSynchronized')
|
||||
|
||||
def isSynchronising(self):
|
||||
return self._request_resource('isSynchronising')
|
||||
|
||||
def closeGB(self):
|
||||
return self._request_resource('closeGB', method='put')
|
||||
|
||||
def openGB(self):
|
||||
return self._request_resource('openGB', method='put')
|
||||
|
||||
def startGenset(self):
|
||||
return self._request_resource('startGenset', method='put')
|
||||
|
||||
def stopGenset(self):
|
||||
return self._request_resource('stopGenset', method='put')
|
||||
|
||||
def setTargetActivePower(self, setpoint):
|
||||
return self._request_resource('setTargetActivePower', setpoint, 'put')
|
||||
|
||||
def setTargetReactivePower(self, setpoint):
|
||||
return self._request_resource('setTargetReactivePower', setpoint, 'put')
|
||||
|
||||
def getActivePower(self):
|
||||
return self._request_resource('getActivePower')
|
||||
|
||||
def getReactivePower(self):
|
||||
return self._request_resource('getReactivePower')
|
||||
|
||||
#TODO: Implement
|
||||
def getCurrentGensetMode(self):
|
||||
pass
|
||||
|
||||
#TODO: Implement
|
||||
def getCurrentRunningMode(self):
|
||||
pass
|
||||
|
||||
#TODO: Implement
|
||||
def setGensetMode(self, mode):
|
||||
pass
|
||||
|
||||
#TODO: Implement
|
||||
def setRunningMode(self, mode):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def getAvailableDieselGenerators():
|
||||
return list(DieselGenerator.__UNITS.keys())
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
from ..core.SyslabUnit import SyslabUnit
|
||||
import warnings
|
||||
|
||||
class Dumpload(SyslabUnit):
|
||||
"""The Dumpload class represents a dumpload in SYSLAB.
|
||||
The Dumpload class is instantiated using a string with the unique name of the dumpload, ie. 'which'
|
||||
|
||||
A full list of available dumploads can be found by calling 'Dumpload.getAvailableDumpLoads()'
|
||||
|
||||
Alternatively, the user may specify a host and port to connect to via the *host* and *port* arguments.
|
||||
"""
|
||||
|
||||
|
||||
__DUMPLOADS = {
|
||||
'mobload1': ('syslab-16.syslab.dk', '8080', 'mobload1'),
|
||||
'mobload2': ('syslab-17.syslab.dk', '8080', 'mobload2'),
|
||||
'mobload3': ('syslab-18.syslab.dk', '8080', 'mobload3'),
|
||||
'load1': ('syslab-05.syslab.dk', '8080', 'load1'),
|
||||
'vmobload1': ('simlab-16', '8080', 'mobload1'),
|
||||
'vmobload2': ('simlab-17', '8080', 'mobload2'),
|
||||
'vmobload3': ('simlab-18', '8080', 'mobload3'),
|
||||
'vload1': ('simlab-05', '8080', 'load1'),
|
||||
'simlab-05': ('192.168.0.105', '8080', 'mobload2'),
|
||||
'simlab-11': ('192.168.0.111', '8080', 'mobload1'),
|
||||
'simlab-12': ('192.168.0.112', '8080', 'load1'),
|
||||
}
|
||||
|
||||
def __init__(self, which=None, host=None, port=None, unitname=None):
|
||||
|
||||
baseurl = 'http://{host}:{port}/typebased_WebService_Load/GenericLoadWebService/{unit_name}/'
|
||||
super().__init__(
|
||||
baseurl=baseurl,
|
||||
which=which,
|
||||
units=self.__DUMPLOADS,
|
||||
host=host,
|
||||
port=port,
|
||||
unit_name=unitname,
|
||||
unit_type="Battery")
|
||||
|
||||
def startLoad(self):
|
||||
return self._request_resource('startLoad', (), 'put')
|
||||
|
||||
def stopLoad(self):
|
||||
return self._request_resource('stopLoad', (), 'put')
|
||||
|
||||
def getPowerSetPoint(self):
|
||||
return self._request_resource('getConstantP')
|
||||
|
||||
def setPowerSetPoint(self, setPoint):
|
||||
"""
|
||||
Set the active power setpoint for the load.
|
||||
|
||||
Inputs:
|
||||
setPoint (float): Requested power setpoint
|
||||
Outputs:
|
||||
Ack (bool): Acknowledgement of receiver
|
||||
"""
|
||||
|
||||
P_UpperLimit = 15.0
|
||||
P_LowerLimit = 0
|
||||
|
||||
return self._request_resource('setConstantP', max(P_LowerLimit, min(setPoint, P_UpperLimit)), 'put')
|
||||
|
||||
def getActivePower(self):
|
||||
return self._request_resource('getActivePower')
|
||||
|
||||
def getReactivePower(self):
|
||||
"""
|
||||
Get the reactive power draw from the load.
|
||||
|
||||
Outputs:
|
||||
Q (CompositeMeasurement): Current reactive power draw, calculated from active power draw (see note)
|
||||
|
||||
NOTE: This is a theoretical value calculated from the relation
|
||||
Q = Q_r * sin (pi * P /P_r)
|
||||
where P_r and Q_r are the rated active and reactive power draw.
|
||||
For control purposes, use the measured value from the switchboard
|
||||
instead to get an actual measurement.
|
||||
"""
|
||||
warnings.warn("The output of getReactivePower from the Dumpload class is calculated from the active power draw rather than a measured value. For control purposes, use the measured power draw on the switchboard.")
|
||||
return self._request_resource('getReactivePower')
|
||||
|
||||
def getRatedPower(self):
|
||||
return self._request_resource('getRatedP')
|
||||
|
||||
def getRatedReactivePower(self):
|
||||
return self._request_resource('getRatedQ')
|
||||
|
||||
def getName(self):
|
||||
return self._request_resource('getLoadName')
|
||||
|
||||
def isLoadOn(self):
|
||||
return self._request_resource('isLoadOn')
|
||||
|
||||
@staticmethod
|
||||
def getAvailableDumploads():
|
||||
return list(Dumpload.__DUMPLOADS.keys())
|
||||
|
||||
|
||||
class MobileLoad1(Dumpload):
|
||||
def __init__(self):
|
||||
super(MobileLoad1, self).__init__("mobload1")
|
||||
|
||||
|
||||
class MobileLoad2(Dumpload):
|
||||
def __init__(self):
|
||||
super(MobileLoad2, self).__init__("mobload2")
|
||||
|
||||
|
||||
class MobileLoad3(Dumpload):
|
||||
def __init__(self):
|
||||
super(MobileLoad3, self).__init__("mobload3")
|
||||
|
|
@ -0,0 +1,213 @@
|
|||
from ..core.SyslabUnit import SyslabUnit
|
||||
from ..core.datatypes import CompositeMeasurement
|
||||
from typing import Union
|
||||
from time import time
|
||||
|
||||
def cast_to_cm(m: Union[CompositeMeasurement, float]):
|
||||
if type(m) == float:
|
||||
# TODO: Is there a better way to estimate precision of time.time?
|
||||
request = CompositeMeasurement(m, timestampMicros=time()*1e6, timePrecision=1000)
|
||||
elif type(m) == CompositeMeasurement:
|
||||
request = m
|
||||
else:
|
||||
raise TypeError(f"Unknown request type: {type(m)}")
|
||||
return request
|
||||
|
||||
class EVSE(SyslabUnit):
|
||||
"""The EVSE class represents a charging post in SYSLAB.
|
||||
The EVSE class is instantiated using a string with the unique name of the charging post, ie. 'which'
|
||||
|
||||
A full list of available charging posts can be found by calling 'EVSE.getAvailableChargingPosts()'
|
||||
|
||||
Alternatively, the user may specify a host and port to connect to via the *host* and *port* arguments.
|
||||
"""
|
||||
|
||||
__CHARGINGPOSTS = {
|
||||
'V2G-319': ('syslab-35.syslab.dk', '8080', 'Endesa_V2G'),
|
||||
'EVSE-NEVIC-6': ('10.42.245.96', '8080', 'EVSE_NEVIC_6'),
|
||||
}
|
||||
|
||||
def __init__(self, which=None, host=None, port=None, unitname=None):
|
||||
|
||||
baseurl = 'http://{host}:{port}/typebased_WebService_EVSE/EVSEWebService/{unit_name}/'
|
||||
super().__init__(
|
||||
baseurl=baseurl,
|
||||
which=which,
|
||||
units=self.__CHARGINGPOSTS,
|
||||
host=host,
|
||||
port=port,
|
||||
unit_name=unitname,
|
||||
unit_type="EVSE")
|
||||
|
||||
def isEVPresent(self):
|
||||
return self._request_resource('isEVPresent')
|
||||
|
||||
def canRemoteStartStop(self):
|
||||
return self._request_resource('canRemoteStartStop')
|
||||
|
||||
def canLimitChargePower(self):
|
||||
return self._request_resource('canLimitChargePower')
|
||||
|
||||
def canDischarge(self):
|
||||
return self._request_resource('canDischarge')
|
||||
|
||||
def hasSOC(self):
|
||||
return self._request_resource('hasSOC')
|
||||
|
||||
def hasACMeasurements(self):
|
||||
return self._request_resource('hasACMeasurements')
|
||||
|
||||
def hasDCMeasurements(self):
|
||||
return self._request_resource('hasDCMeasurements')
|
||||
|
||||
def getAvailableOperatingStates(self):
|
||||
return self._request_resource('getAvailableOperatingStates')
|
||||
|
||||
def getCurrentOperatingState(self):
|
||||
return self._request_resource('getCurrentOperatingState')
|
||||
|
||||
def getACActivePower(self):
|
||||
return self._request_resource('getACActivePower')
|
||||
|
||||
def getACReactivePower(self):
|
||||
return self._request_resource('getACReactivePower')
|
||||
|
||||
def getACFrequency(self):
|
||||
return self._request_resource('getACFrequency')
|
||||
|
||||
def getACVoltages(self):
|
||||
return self._request_resource('getACVoltages')
|
||||
|
||||
def getACCurrents(self):
|
||||
return self._request_resource('getACCurrents')
|
||||
|
||||
def getSOC(self):
|
||||
return self._request_resource('getSOC')
|
||||
|
||||
def getMinSOC(self):
|
||||
return self._request_resource('getMinSOC')
|
||||
|
||||
def getMaxSOC(self):
|
||||
return self._request_resource('getMaxSOC')
|
||||
|
||||
def startCharge(self):
|
||||
return self._request_resource('startCharge', (), 'put')
|
||||
|
||||
def stopCharge(self):
|
||||
return self._request_resource('stopCharge', (), 'put')
|
||||
|
||||
def startDischarge(self):
|
||||
return self._request_resource('startDischarge', (), 'put')
|
||||
|
||||
def stopDischarge(self):
|
||||
return self._request_resource('stopDischarge', (), 'put')
|
||||
|
||||
def getMinimumChargePower(self):
|
||||
return self._request_resource('getMinimumChargePower')
|
||||
|
||||
def getMaximumChargePower(self):
|
||||
return self._request_resource('getMaximumChargePower')
|
||||
|
||||
def getMinimumDischargePower(self):
|
||||
return self._request_resource('getMinimumDischargePower')
|
||||
|
||||
def getMaximumDischargePower(self):
|
||||
return self._request_resource('getMaximumDischargePower')
|
||||
|
||||
def setPowerSetpoint(self, setPoint):
|
||||
|
||||
P_UpperLimit = 10.0
|
||||
P_LowerLimit = -10.0
|
||||
|
||||
return self._request_resource('setP', (), 'put', cast_to_cm(max(P_LowerLimit, min(setPoint, P_UpperLimit))).parseToJSON())
|
||||
|
||||
def isPSetpointEnabled(self):
|
||||
return self._request_resource('isPSetpointEnabled')
|
||||
|
||||
def getActiveEnergyImport(self): # TODO not working/implemented
|
||||
return self._request_resource('getActiveEnergyImport')
|
||||
|
||||
def getActiveEnergyExport(self): # TODO not working/implemented
|
||||
return self._request_resource('getActiveEnergyExport')
|
||||
|
||||
def getReactiveEnergyImport(self): # TODO not working/implemented
|
||||
return self._request_resource('getReactiveEnergyImport')
|
||||
|
||||
def getReactiveEnergyExport(self): # TODO not working/implemented
|
||||
return self._request_resource('getReactiveEnergyExport')
|
||||
|
||||
def getNodeConfiguration(self): # TODO not working/implemented
|
||||
return self._request_resource('getNodeConfiguration')
|
||||
|
||||
def getEVSEName(self):
|
||||
return self._request_resource('getEVSEName')
|
||||
|
||||
def getEVSELogicalNameplate(self): # TODO not working/implemented
|
||||
return self._request_resource('getEVSELogicalNameplate')
|
||||
|
||||
def getEVSEPhysicalNameplate(self): # TODO not working/implemented
|
||||
return self._request_resource('getEVSEPhysicalNameplate')
|
||||
|
||||
def getEVSEHealth(self): # TODO not working/implemented
|
||||
return self._request_resource('getEVSEHealth')
|
||||
|
||||
def getGPSLocation(self): # TODO not working/implemented
|
||||
return self._request_resource('getGPSLocation')
|
||||
|
||||
def getNumberOfEvents(self): # TODO not working/implemented
|
||||
return self._request_resource('getNumberOfEvents')
|
||||
|
||||
def getEvents(self): # TODO not working/implemented
|
||||
return self._request_resource('getEvents')
|
||||
|
||||
def getNumberOfAlarms(self): # TODO not working/implemented
|
||||
return self._request_resource('getNumberOfAlarms')
|
||||
|
||||
def getAlarms(self): # TODO not working/implemented
|
||||
return self._request_resource('getAlarms')
|
||||
|
||||
def getNumberOfUnacknowledgedAlarms(self): # TODO not working/implemented
|
||||
return self._request_resource('getNumberOfUnacknowledgedAlarms')
|
||||
|
||||
def getUnacknowledgedAlarms(self): # TODO not working/implemented
|
||||
return self._request_resource('getUnacknowledgedAlarms')
|
||||
|
||||
def acknowledgeAlarms(self): # TODO not working/implemented
|
||||
return self._request_resource('acknowledgeAlarms')
|
||||
|
||||
def getRatedP(self):
|
||||
return self._request_resource('getRatedP')
|
||||
|
||||
def getRatedQ(self):
|
||||
return self._request_resource('getRatedQ')
|
||||
|
||||
def getRatedS(self):
|
||||
return self._request_resource('getRatedS')
|
||||
|
||||
def getRatedU(self):
|
||||
return self._request_resource('getRatedU')
|
||||
|
||||
def getRatedf(self):
|
||||
return self._request_resource('getRatedf')
|
||||
|
||||
def getInverterName(self): # TODO not working/implemented
|
||||
return self._request_resource('getInverterName')
|
||||
|
||||
def getInverterLogicalNameplate(self): # TODO not working/implemented
|
||||
return self._request_resource('getInverterLogicalNameplate')
|
||||
|
||||
def getInverterHealth(self): # TODO not working/implemented
|
||||
return self._request_resource('getInverterHealth')
|
||||
|
||||
def getDCVoltage(self): # TODO not working/implemented
|
||||
return self._request_resource('getDCVoltage')
|
||||
|
||||
def getDCPower(self): # TODO not working/implemented
|
||||
return self._request_resource('getDCPower')
|
||||
|
||||
def isDCContactorClosed(self): # TODO not working/implemented
|
||||
return self._request_resource('isDCContactorClosed')
|
||||
|
||||
@staticmethod
|
||||
def getAvailableEVSE():
|
||||
return list(EVSE.__CHARGINGPOSTS.keys())
|
||||
|
|
@ -0,0 +1,164 @@
|
|||
from ..core.SyslabUnit import SyslabUnit
|
||||
from ..core.datatypes import CompositeMeasurement, HeatCirculationPumpMode, HeatCirculationPumpState
|
||||
|
||||
|
||||
class Valve:
|
||||
"""
|
||||
Convenience class which calls methods on its associated heatswitchboard
|
||||
"""
|
||||
def __init__(self, label, heatswitchboard):
|
||||
self.label = label
|
||||
self.heatswitchboard = heatswitchboard
|
||||
#TODO: Add methods
|
||||
|
||||
class Pump:
|
||||
"""
|
||||
Convenience class which calls methods on its associated heatswitchboard
|
||||
"""
|
||||
def __init__(self, label, heatswitchboard):
|
||||
self.label = label
|
||||
self.heatswitchboard = heatswitchboard
|
||||
#TODO: Add methods
|
||||
|
||||
class Meter:
|
||||
"""
|
||||
Convenience class which calls methods on its associated heatswitchboard
|
||||
"""
|
||||
def __init__(self, label, heatswitchboard):
|
||||
self.label = label
|
||||
self.heatswitchboard = heatswitchboard
|
||||
#TODO: Add methods
|
||||
|
||||
|
||||
class HeatSwitchBoard(SyslabUnit):
|
||||
"""
|
||||
The HeatSwitchBoard class represents a HeatSwitchBoard in SYSLAB.
|
||||
The HeatSwitchBoard class is instantiated using a string with the unique name of the switchboard, ie. 'which'
|
||||
|
||||
A full list of available switchboards can be found by calling 'SwitchBoard.getAvailableSwitchBoards()'
|
||||
|
||||
Alternatively, the user may specify a host and port to connect to via the *host* and *port* arguments.
|
||||
"""
|
||||
|
||||
__HEAT_SWITCH_BOARDS = {
|
||||
'716-h1': ('syslab-33.syslab.dk', '8080', '716-h1'),
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def getAvailableHeatSwitchBoards():
|
||||
return list(HeatSwitchBoard.__HEAT_SWITCH_BOARDS.keys())
|
||||
|
||||
|
||||
def __init__(self, which=None, host=None, port=None, unitname=None):
|
||||
|
||||
|
||||
baseurl = f'http://{host}:{port}/typebased_WebService_HeatSubstation/HeatSwitchboardWebService/{unit_name}/'
|
||||
super().__init__(
|
||||
baseurl=baseurl,
|
||||
which=which,
|
||||
units=self.__HEAT_SWITCH_BOARDS,
|
||||
host=host,
|
||||
port=port,
|
||||
unit_name=unitname,
|
||||
unit_type="HeatSwitchBoard")
|
||||
|
||||
# Inventory functions
|
||||
|
||||
#TODO Handle these in core/SYSLAB_unit.py by properly handling arrays. This is a bodge for now.
|
||||
def getHeatMeters(self):
|
||||
result = self._request_resource('getHeatMeters', check_types=False)
|
||||
return result
|
||||
|
||||
#TODO, P1: Awaiting Valve[]
|
||||
def getValves(self):
|
||||
raise NotImplementedError
|
||||
|
||||
# Valve functions
|
||||
|
||||
def getValvePosition(self, valve) -> CompositeMeasurement:
|
||||
return self._request_resource('getValvePosition', valve)
|
||||
|
||||
#TODO: Implement
|
||||
#def setValvePosition(self, valve: str, position: float, timestamp=0):
|
||||
def setValvePosition(self, valve: str, position: CompositeMeasurement):
|
||||
"""
|
||||
Sets *valve* to *position*. Position must be float in the
|
||||
interval from 0.0 to 1.0
|
||||
Position = 0.0 => *valve* fully closed
|
||||
Position = 1.0 => *valve* fully open
|
||||
"""
|
||||
assert 0.0 <= position.value and position.value <= 1.0
|
||||
return self._request_resource('setValvePosition', (valve), 'put',
|
||||
position.parseToJSON())
|
||||
#CompositeMeasurement(value=position, timestampMicros=timestamp).parseToJSON())
|
||||
|
||||
# Meter functions
|
||||
|
||||
def getBackTemperature(self, meter) -> CompositeMeasurement:
|
||||
return self._request_resource('getBackTemperature', meter)
|
||||
|
||||
def getFlow(self, meter) -> CompositeMeasurement:
|
||||
return self._request_resource('getFlow', meter)
|
||||
|
||||
def getThermalPower(self, meter) -> CompositeMeasurement:
|
||||
return self._request_resource('getThermalPower', meter)
|
||||
|
||||
def getFwdTemperature(self, meter) -> CompositeMeasurement:
|
||||
return self._request_resource('getFwdTemperature', meter)
|
||||
|
||||
def getPressure(self, meter) -> CompositeMeasurement:
|
||||
return self._request_resource('getPressure', meter)
|
||||
|
||||
def getVolume(self, meter) -> CompositeMeasurement:
|
||||
return self._request_resource('getVolume', meter)
|
||||
|
||||
def getMass(self, meter) -> CompositeMeasurement:
|
||||
return self._request_resource('getMass', meter)
|
||||
|
||||
def getHeatEnergy(self, meter) -> CompositeMeasurement:
|
||||
return self._request_resource('getHeatEnergy', meter)
|
||||
|
||||
def getCoolingEnergy(self, meter) -> CompositeMeasurement:
|
||||
return self._request_resource('getCoolingEnergy', meter)
|
||||
|
||||
# Pump functions
|
||||
|
||||
def getPumpState(self, pump) -> HeatCirculationPumpState:
|
||||
return self._request_resource('getPumpState', pump)
|
||||
|
||||
def getPumpControlMode(self, pump) -> HeatCirculationPumpMode:
|
||||
return self._request_resource('getPumpControlMode', pump)
|
||||
|
||||
def setPumpControlMode(self, pump, mode: HeatCirculationPumpMode):
|
||||
return self._request_resource('setPumpControlMode', (pump), 'put', mode.parseToJSON())
|
||||
|
||||
def setPumpMaxFlow(self, pump, limit: CompositeMeasurement):
|
||||
return self._request_resource('setPumpMaxFlow', (pump), 'put', limit.parseToJSON())
|
||||
|
||||
def startPump(self, pump):
|
||||
return self._request_resource('startPump', pump)
|
||||
|
||||
def stopPump(self, pump):
|
||||
return self._request_resource('stopPump', pump)
|
||||
|
||||
#TODO: Split into three functions that check against current pump mode.
|
||||
def setPumpSetpoint(self, pump, setpoint: CompositeMeasurement):
|
||||
"""
|
||||
Sets the target for the pump.
|
||||
NOTE: How to interpret the setpoint depends on the mode which the pump is in.
|
||||
Here be dragons.
|
||||
"""
|
||||
return self._request_resource('setPumpSetpoint', (pump), 'put', setpoint.parseToJSON())
|
||||
|
||||
def getPumpHead(self, pump) -> CompositeMeasurement:
|
||||
return self._request_resource('getPumpHead', pump)
|
||||
|
||||
def getPumpFlow(self, pump) -> CompositeMeasurement:
|
||||
return self._request_resource('getPumpFlow', pump)
|
||||
|
||||
def getPumpRPM(self, pump) -> CompositeMeasurement:
|
||||
return self._request_resource('getPumpRPM', pump)
|
||||
|
||||
def getPumpRelPerformance(self, pump) -> CompositeMeasurement:
|
||||
return self._request_resource('getPumpPerformance', pump)
|
||||
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
from ..core.SyslabUnit import SyslabUnit
|
||||
|
||||
|
||||
class MeteoMast(SyslabUnit):
|
||||
__METMASTS = {
|
||||
'metmast1': ('syslab-13.syslab.dk', '8080', 'meteo1'),
|
||||
}
|
||||
|
||||
def __init__(self, which=None, host=None, port=None, unitname=None):
|
||||
|
||||
baseurl = 'http://{host}:{port}/typebased_WebService_Meteo/MeteoStationWebService/{unit_name}/'
|
||||
super().__init__(
|
||||
baseurl=baseurl,
|
||||
which=which,
|
||||
units=self.__METMASTS,
|
||||
host=host,
|
||||
port=port,
|
||||
unit_name=unitname,
|
||||
unit_type="MeteoMast")
|
||||
|
||||
|
||||
|
||||
def getAirPressure(self, instrumentIndex: int or str):
|
||||
if type(instrumentIndex) is str:
|
||||
return self._request_resource('getAirPressure2', instrumentIndex)
|
||||
if type(instrumentIndex) is int:
|
||||
return self._request_resource('getAirPressure1', instrumentIndex)
|
||||
|
||||
def getAirTemperature(self, instrumentIndex: int or str):
|
||||
if type(instrumentIndex) is str:
|
||||
return self._request_resource('getAirTemperature2', instrumentIndex)
|
||||
if type(instrumentIndex) is int:
|
||||
return self._request_resource('getAirTemperature1', instrumentIndex)
|
||||
|
||||
def getHeightAboveGround(self):
|
||||
return self._request_resource('getHeightAboveGround')
|
||||
|
||||
def getInsolation(self, instrumentIndex: int or str):
|
||||
if type(instrumentIndex) is str:
|
||||
return self._request_resource('getInsolation2', instrumentIndex)
|
||||
if type(instrumentIndex) is int:
|
||||
return self._request_resource('getInsolation1', instrumentIndex)
|
||||
|
||||
def getMeteoGPSLocation(self):
|
||||
return self._request_resource('getMeteoGPSLocation')
|
||||
|
||||
def getRelativeHumidity(self, instrumentIndex: int or str):
|
||||
if type(instrumentIndex) is str:
|
||||
return self._request_resource('getRelativeHumidity2', instrumentIndex)
|
||||
if type(instrumentIndex) is int:
|
||||
return self._request_resource('getRelativeHumidity1', instrumentIndex)
|
||||
|
||||
def getWindDirection(self, instrumentIndex: int or str):
|
||||
if type(instrumentIndex) is str:
|
||||
return self._request_resource('getWindDirection2', instrumentIndex)
|
||||
if type(instrumentIndex) is int:
|
||||
return self._request_resource('getWindDirection1', instrumentIndex)
|
||||
|
||||
def getWindSpeed(self, instrumentIndex: int or str):
|
||||
if type(instrumentIndex) is str:
|
||||
return self._request_resource('getWindSpeed2', instrumentIndex)
|
||||
if type(instrumentIndex) is int:
|
||||
return self._request_resource('getWindSpeed1', instrumentIndex)
|
||||
|
||||
def getInstrumentNames(self):
|
||||
return self._request_resource('getInstrumentNames')
|
||||
|
||||
def getSupportedInstrumentTypes(self):
|
||||
return self._request_resource('getSupportedInstrumentTypes')
|
||||
|
||||
def getInstrumentNamesForType(self, instrumentType: str):
|
||||
return self._request_resource('getInstrumentNamesForType', instrumentType)
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
from ..core.SyslabUnit import SyslabUnit
|
||||
|
||||
|
||||
class Photovoltaics(SyslabUnit):
|
||||
"""The Photovoltaics class represents a photovoltaic panel array in SYSLAB.
|
||||
The Photovoltaics class is instantiated using a string with the unique name of the dumpload, ie. 'which'
|
||||
|
||||
A full list of available panel arrays can be found by calling 'Photovoltaics.getAvailablePhotovoltaics()'
|
||||
|
||||
Alternatively, the user may specify a host and port to connect to via the *host* and *port* arguments.
|
||||
"""
|
||||
|
||||
__PHOTOVOLTAICS = {
|
||||
'pv319': ('syslab-24.syslab.dk', '8080', 'pv319'),
|
||||
'pv715': ('syslab-10.syslab.dk', '8080', 'pv715'),
|
||||
'pv117': ('syslab-07.syslab.dk', '8080', 'pv117'),
|
||||
'simlab-03': ('192.168.0.103', '8080', 'pv715'),
|
||||
'simlab-13': ('192.168.0.113', '8080', 'pv319'),
|
||||
'vpv319': ('simlab-24', '8080', 'pv319'),
|
||||
'vpv715': ('simlab-10', '8080', 'pv715'),
|
||||
}
|
||||
|
||||
def __init__(self, which=None, host=None, port=None, unitname=None):
|
||||
|
||||
baseurl = 'http://{host}:{port}/typebased_WebService_PV/PVSystemWebService/{unit_name}/'
|
||||
super().__init__(
|
||||
baseurl=baseurl,
|
||||
which=which,
|
||||
units=self.__PHOTOVOLTAICS,
|
||||
host=host,
|
||||
port=port,
|
||||
unit_name=unitname,
|
||||
unit_type="Photovoltaics")
|
||||
|
||||
def getACActivePower(self):
|
||||
return self._request_resource('getACActivePower')
|
||||
|
||||
def getACReactivePower(self):
|
||||
return self._request_resource('getACReactivePower')
|
||||
|
||||
def getPacLimit(self):
|
||||
return self._request_resource('getPacLimit')
|
||||
|
||||
def getQSetpoint(self):
|
||||
return self._request_resource('getQSetpoint')
|
||||
|
||||
def getRatedPower(self):
|
||||
return self._request_resource('getRatedP')
|
||||
|
||||
def getName(self):
|
||||
return self._request_resource('getSystemName')
|
||||
|
||||
def setPacLimit(self, setPoint):
|
||||
return self._request_resource('setPacLimit', setPoint, 'put')
|
||||
|
||||
def setQSetpoint(self, Q):
|
||||
return self._request_resource('setQ', Q, 'put')
|
||||
|
||||
@staticmethod
|
||||
def getAvailablePhotovoltaics():
|
||||
return list(Photovoltaics.__PHOTOVOLTAICS.keys())
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
from ..core.SyslabUnit import SyslabUnit
|
||||
|
||||
|
||||
class SwitchBoard(SyslabUnit):
|
||||
"""
|
||||
The SwitchBoard class represents a SwitchBoard in SYSLAB.
|
||||
The SwitchBoard class is instantiated using a string with the unique name of the switchboard, ie. 'which'
|
||||
|
||||
A full list of available switchboards can be found by calling 'SwitchBoard.getAvailableSwitchBoards()'
|
||||
|
||||
Alternatively, the user may specify a host and port to connect to via the *host* and *port* arguments.
|
||||
"""
|
||||
|
||||
__SWITCH_BOARDS = {
|
||||
'319-2':('syslab-01.syslab.dk', '8080', '319-2'),
|
||||
'319-3':('syslab-52.syslab.dk', '8080', '319-3'),
|
||||
'117-2':('syslab-11.syslab.dk', '8080', '117-2'),
|
||||
'117-4':('syslab-11.syslab.dk', '8080', '117-4'),
|
||||
'117-5':('syslab-11.syslab.dk', '8080', '117-5'),
|
||||
'117-6':('syslab-26.syslab.dk', '8080', '117-6'),
|
||||
'715-2':('syslab-09.syslab.dk', '8080', '715-2'),
|
||||
'716-2':('syslab-29.syslab.dk', '8080', '716-2'),
|
||||
'simlab-00': ('192.168.0.1', '8080', '319-2'),
|
||||
'simlab-10': ('192.168.0.2', '8080', '319-2'),
|
||||
'simlab-20': ('192.168.0.3', '8080', '319-2'),
|
||||
'vswitchboard': ('simlab-01', '8080', '319-2')
|
||||
}
|
||||
|
||||
def __init__(self, which=None, host=None, port=None, unitname=None):
|
||||
|
||||
baseurl = 'http://{host}:{port}/typebased_WebService_Substation/StandardSubstationWebService/{unit_name}/'
|
||||
super().__init__(
|
||||
baseurl=baseurl,
|
||||
which=which,
|
||||
units=self.__SWITCH_BOARDS,
|
||||
host=host,
|
||||
port=port,
|
||||
unit_name=unitname,
|
||||
unit_type="SwitchBoard")
|
||||
|
||||
def getName(self):
|
||||
return self._request_resource('getSwitchboardName')
|
||||
|
||||
def getActivePower(self, instrName):
|
||||
return self._request_resource('getActivePower', instrName)
|
||||
|
||||
def getPhaseActivePower(self, instrName):
|
||||
return self._request_resource('getPhaseActivePower', instrName)
|
||||
|
||||
def getReactivePower(self, instrName):
|
||||
return self._request_resource('getReactivePower', instrName)
|
||||
|
||||
def getPhaseReactivePower(self, instrName):
|
||||
return self._request_resource('getPhaseReactivePower', instrName)
|
||||
|
||||
def getApparentPower(self, instrName):
|
||||
return self._request_resource('getApparentPower', instrName)
|
||||
|
||||
def getPhaseApparentPower(self, instrName):
|
||||
return self._request_resource('getPhaseApparentPower', instrName)
|
||||
|
||||
def getPowerFactor(self, instrName):
|
||||
return self._request_resource('getPowerFactor', instrName)
|
||||
|
||||
def getPhasePowerFactor(self, instrName):
|
||||
return self._request_resource('getPhasePowerFactor', instrName)
|
||||
|
||||
def getActiveEnergyExport(self, instrName):
|
||||
return self._request_resource('getActiveEnergyExport', instrName)
|
||||
|
||||
def getActiveEnergyImport(self, instrName):
|
||||
return self._request_resource('getActiveEnergyImport', instrName)
|
||||
|
||||
def getReactiveEnergyExport(self, instrName):
|
||||
return self._request_resource('getReactiveEnergyExport', instrName)
|
||||
|
||||
def getReactiveEnergyImport(self, instrName):
|
||||
return self._request_resource('getReactiveEnergyImport', instrName)
|
||||
|
||||
def isAuthenticated(self):
|
||||
return self._request_resource('isAuthenticated')
|
||||
|
||||
def getBreakerState(self, breakerName):
|
||||
return self._request_resource('getBreakerState', breakerName)
|
||||
|
||||
def getFrequency(self, instrName):
|
||||
return self._request_resource('getFrequency', instrName)
|
||||
|
||||
def getBayNames(self):
|
||||
return self._request_resource('getBayNames')
|
||||
|
||||
def getBreakerName(self, busbarName, bayName):
|
||||
return self._request_resource('getBreakerName', (busbarName, bayName))
|
||||
|
||||
def getInstrumentNames(self, bayName):
|
||||
return self._request_resource('getInstrumentNamesPerBay', bayName)
|
||||
|
||||
def getBusbarNames(self):
|
||||
return self._request_resource('getBusbarNames')
|
||||
|
||||
def getInterphaseVoltage(self, instrName):
|
||||
return self._request_resource('getInterphaseVoltage', instrName)
|
||||
|
||||
def getInterphaseVoltages(self, instrName):
|
||||
return self._request_resource('getInterphaseVoltages', instrName)
|
||||
|
||||
def getPhaseVoltage(self, instrName):
|
||||
return self._request_resource('getPhaseVoltage', instrName)
|
||||
|
||||
def getPhaseVoltages(self, instrName):
|
||||
return self._request_resource('getPhaseVoltages', instrName)
|
||||
|
||||
def getVoltageImbalance(self, instrName):
|
||||
return self._request_resource('getVoltageImbalance', instrName)
|
||||
|
||||
def getPhaseCurrent(self, instrName):
|
||||
return self._request_resource('getPhaseCurrent', instrName)
|
||||
|
||||
def getNeutralCurrent(self, instrName):
|
||||
return self._request_resource('getNeutralCurrent', instrName)
|
||||
|
||||
def getPhaseCurrents(self, instrName):
|
||||
return self._request_resource('getPhaseCurrents', instrName)
|
||||
|
||||
def getCurrentImbalance(self, instrName):
|
||||
return self._request_resource('getCurrentImbalance', instrName)
|
||||
|
||||
def authenticate(self, user, password):
|
||||
return self._request_resource('authenticate', (user, password), 'put')
|
||||
|
||||
def logout(self):
|
||||
return self._request_resource('logout', (), 'put')
|
||||
|
||||
def closeBreaker(self, breakerName):
|
||||
return self._request_resource('closeBreaker', breakerName, 'put')
|
||||
|
||||
def openBreaker(self, breakerName):
|
||||
return self._request_resource('openBreaker', (breakerName), 'put')
|
||||
|
||||
@staticmethod
|
||||
def getAvailableSwitchBoards():
|
||||
return list(SwitchBoard.__SWITCH_BOARDS.keys())
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
from ..core.SyslabUnit import SyslabUnit
|
||||
|
||||
|
||||
class WindTurbine(SyslabUnit):
|
||||
__TURBINES = {
|
||||
'gaia1': ('syslab-03.syslab.dk', '8080', 'gaia1'),
|
||||
'simlab-01': ('192.168.0.101', '8080', 'gaia1'),
|
||||
'vgaia1': ('simlab-03', '8080', 'gaia1'),
|
||||
}
|
||||
|
||||
def __init__(self, which=None, host=None, port=None, unitname=None):
|
||||
|
||||
baseurl = 'http://{host}:{port}/typebased_WebService_WTGS/GaiaWindTurbineWebService/{unit_name}/'
|
||||
super().__init__(
|
||||
baseurl=baseurl,
|
||||
which=which,
|
||||
units=self.__TURBINES,
|
||||
host=host,
|
||||
port=port,
|
||||
unit_name=unitname,
|
||||
unit_type="WindTurbine")
|
||||
|
||||
def getGeneratorName(self):
|
||||
return self._request_resource('getGeneratorName')
|
||||
|
||||
def getName(self):
|
||||
return self.getGeneratorName()
|
||||
|
||||
def getActivePower(self):
|
||||
return self._request_resource('getActivePower')
|
||||
|
||||
def getReactivePower(self):
|
||||
return self._request_resource('getReactivePower')
|
||||
|
||||
def getWindspeed(self):
|
||||
return self._request_resource('getWindspeedOutsideNacelle')
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
from .Battery import Battery
|
||||
from .DieselGenerator import DieselGenerator
|
||||
from .Dumpload import Dumpload
|
||||
from .HeatSwitchBoard import HeatSwitchBoard
|
||||
from .MeteoMast import MeteoMast
|
||||
from .Photovoltaics import Photovoltaics
|
||||
from .SwitchBoard import SwitchBoard
|
||||
from .WindTurbine import WindTurbine
|
||||
from .B2BConverter import B2BConverter
|
||||
from .EVSE import EVSE
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
class FlexHouse_real():
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def setActivePower(self, power_reference):
|
||||
if power_reference < -10:
|
||||
power_reference = -10
|
||||
|
||||
if power_reference > 0:
|
||||
raise ValueError("Positive power means production")
|
||||
self.__setValue('flexHousePowerRef_kW',-power_reference)
|
||||
|
||||
def getTemperature(self):
|
||||
return self.__getValue('flexHouseTemperature_C')
|
||||
|
||||
def getActivePower(self):
|
||||
return self.__getValue('flexHousePower_kW')
|
||||
|
||||
def __getValue(self, key):
|
||||
from requests import get, auth
|
||||
import re
|
||||
|
||||
if not type(key) is str: raise TypeError('Key should be a string, found {0}'.format(type(key)))
|
||||
if key is '': raise TypeError('Key should be an empty string')
|
||||
|
||||
url = 'http://whiteboard.syslab.dk/wbget.php'
|
||||
r = get(url, auth=auth.HTTPBasicAuth('twinPV99', 'twinPV99'))
|
||||
entries = r.text.split('\n')
|
||||
|
||||
result = None
|
||||
|
||||
for entry in entries:
|
||||
entry = entry.rstrip().lstrip()
|
||||
g = re.match('SYSLAB@(\d+):{0}=(.+);'.format(key), entry)
|
||||
if g is None:
|
||||
continue
|
||||
else:
|
||||
g = g.groups()
|
||||
result = g[1]
|
||||
break
|
||||
|
||||
return result
|
||||
|
||||
def __setValue(selv, key, value):
|
||||
from requests import post, auth
|
||||
url = '{0}{1}?source=SYSLAB&{2}={3}'.format('http://whiteboard.syslab.dk/', 'wbset.php', key, str(value))
|
||||
post(url, auth=auth.HTTPBasicAuth('twinPV99', 'twinPV99'))
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
from ..physical.Dumpload import Dumpload
|
||||
from ..virtual.MetMast import MetMast
|
||||
from datetime import datetime
|
||||
|
||||
class FlexHouse_sim(Dumpload):
|
||||
def __init__(self, which='localhost'):
|
||||
super().__init__(which)
|
||||
self.internal_temperature = 20 # Celsius
|
||||
self.time = datetime.now()
|
||||
self.mt = MetMast()
|
||||
|
||||
# The variables below are only relevant for implementing an advanced house model.
|
||||
#self.__Th = 20 # internal heater state
|
||||
#self.__Te = 19 # envelope state
|
||||
#self.__Aw = 14.351 # m^2
|
||||
#self.__Ce = 4.741 # kWh/C
|
||||
#self.__Ch = 0.00225 # kWh/Cst
|
||||
#self.__Ci = 2.555 # kWh/C
|
||||
#self.__Rea = 3.265 # C/kW
|
||||
#self.__Rie = 0.817 # C/kW
|
||||
#self.__Ria = 37.005 # C/kW
|
||||
#self.__Rih = 140.44 # C/kW
|
||||
|
||||
def setPowerConsumption(self, power_reference):
|
||||
if power_reference > 10:
|
||||
self.startLoad()
|
||||
power_reference = 10.0
|
||||
if power_reference < 0:
|
||||
raise ValueError("Negative power means production")
|
||||
if power_reference == 0:
|
||||
self.stopLoad()
|
||||
self.setPowerSetPoint(power_reference)
|
||||
|
||||
def step_sim(self):
|
||||
time_now = datetime.now()
|
||||
time_delta = (time_now - self.time).total_seconds()
|
||||
T_ambient = self.mt.getTemperature()
|
||||
irradiance = self.mt.getInsolation()/1000
|
||||
|
||||
# Below is a simple house model. A more complex one can be implemented
|
||||
self.internal_temperature += 0.5*(self.getActivePower().value/3600*time_delta) - 0.00025*((self.internal_temperature-T_ambient)*time_delta) + 0.001*irradiance*time_delta
|
||||
#self.__Th = 20 # internal heater state
|
||||
#self.__Te = 19 # envelope state
|
||||
self.time = time_now
|
||||
|
||||
def getTemperature(self):
|
||||
self.step_sim()
|
||||
return self.internal_temperature
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
from ..physical.Battery import Battery
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class FlexHouse_sim_batt(Battery):
|
||||
def __init__(self, which='localhost'):
|
||||
super().__init__(which)
|
||||
self.internal_temperature = 20 # Celsius
|
||||
self.time = datetime.now()
|
||||
self.loss = 1 # kW
|
||||
if self.getRemainingFloodTime() == 300:
|
||||
#print('Battery is not started. I will initiate start sequence, please retry in 5 minutes')
|
||||
self.startBattery()
|
||||
raise SystemError('Battery is not started. I will initiate start sequence, please retry in 5 minutes')
|
||||
if (self.getRemainingFloodTime() > 1) and (self.getRemainingFloodTime() < 300):
|
||||
#print('Battery is not ready yet')
|
||||
raise SystemError('Battery is not ready yet, please wait {} seconds'.format(self.getRemainingFloodTime()))
|
||||
|
||||
# The variables below are only relevant for implementing and advanced house model.
|
||||
#self.__Th = 20 # internal heater state
|
||||
#self.__Te = 19 # envelope state
|
||||
#self.__Aw = 14.351 # m^2
|
||||
#self.__Ce = 4.741 # kWh/C
|
||||
#self.__Ch = 0.00225 # kWh/C
|
||||
#self.__Ci = 2.555 # kWh/C
|
||||
#self.__Rea = 3.265 # C/kW
|
||||
#self.__Rie = 0.817 # C/kW
|
||||
#self.__Ria = 37.005 # C/kW
|
||||
#self.__Rih = 140.44 # C/kW
|
||||
|
||||
def setPowerConsumption(self, power_reference):
|
||||
if power_reference > 10:
|
||||
power_reference = 10
|
||||
if power_reference < 0:
|
||||
raise ValueError("Negative power means production")
|
||||
self.setActivePower(power_reference)
|
||||
|
||||
def step_sim(self):
|
||||
time_now = datetime.now()
|
||||
time_delta = (time_now - self.time).total_seconds()
|
||||
# Below is a simple house model. A more complex one can be implemented
|
||||
self.internal_temperature += 0.5*(self.getActivePower().value/3600*time_delta) - 2*(self.loss/3600*time_delta)
|
||||
#self.__Th = 20 # internal heater state
|
||||
#self.__Te = 19 # envelope state
|
||||
self.time = time_now
|
||||
|
||||
def getTemperature(self):
|
||||
self.step_sim()
|
||||
return self.internal_temperature
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
class MetMast():
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def getTemperature(self):
|
||||
return self.__getValue('OutsideTemperature')
|
||||
|
||||
def getInsolation(self):
|
||||
return self.__getValue('Insolation')
|
||||
|
||||
def getWindDirection(self):
|
||||
return self.__getValue('WindDirection')
|
||||
|
||||
def getWindSpeed(self):
|
||||
return self.__getValue('WindSpeed')
|
||||
|
||||
def __getValue(self, key):
|
||||
from requests import get, auth
|
||||
import re
|
||||
|
||||
if not type(key) is str: raise TypeError('Key should be a string, found {0}'.format(type(key)))
|
||||
assert key is not '', 'Key should not be an empty string'
|
||||
|
||||
url = 'http://whiteboard.syslab.dk/wbget.php'
|
||||
r = get(url, auth=auth.HTTPBasicAuth('twinPV99', 'twinPV99'))
|
||||
entries = r.text.split('\n')
|
||||
|
||||
result = None
|
||||
|
||||
for entry in entries:
|
||||
entry = entry.rstrip().lstrip()
|
||||
g = re.match('SYSLAB@(\d+):{0}=(.+);'.format(key), entry)
|
||||
if g is None:
|
||||
continue
|
||||
else:
|
||||
g = g.groups()
|
||||
result = g[1]
|
||||
break
|
||||
|
||||
return result
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
from ..physical.Dumpload import Dumpload
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class WaterBoiler(Dumpload):
|
||||
|
||||
def __init__(self, which='localhost'):
|
||||
super().__init__(which)
|
||||
|
||||
self.energy_state = 10 # kWh
|
||||
self.energy_max = 15
|
||||
self.time = datetime.now()
|
||||
self.loss = 1 # kW
|
||||
|
||||
def step_sim(self):
|
||||
time_now = datetime.now()
|
||||
time_delta = (time_now - self.time).total_seconds()
|
||||
power = self.getActivePower().value
|
||||
self.energy_state += (power - self.loss)/3600*time_delta
|
||||
self.time = time_now
|
||||
|
||||
def getSOC(self):
|
||||
self.step_sim()
|
||||
if self.energy_state > self.energy_max:
|
||||
self.energy_state = self.energy_max
|
||||
elif self.energy_state < 0:
|
||||
self.energy_state = 0.0
|
||||
return self.energy_state
|
||||
|
|
@ -0,0 +1 @@
|
|||
from . import *
|
||||
|
|
@ -0,0 +1,143 @@
|
|||
from syslab.whiteboard.WhiteBoardEntry import WhiteBoardEntry
|
||||
|
||||
class CommModule():
|
||||
__BASE_URL = 'http://whiteboard.syslab.dk/'
|
||||
|
||||
def __init__(self, namespace):
|
||||
if not type(namespace) is str or len(namespace) == 0: raise TypeError('Namespace should be an non-empty string, found "{0}"'.format(type(namespace)))
|
||||
|
||||
self.__namespace = namespace
|
||||
self.__entries = {}
|
||||
|
||||
def appendValue(self, key, value):
|
||||
values = self.getList(key)
|
||||
|
||||
if values == None:
|
||||
self.publishToWhiteBoardServer(key, '[{0}]'.format(str(value)))
|
||||
else:
|
||||
values.append(value)
|
||||
self.publishList(key, values)
|
||||
|
||||
def getList(self, key):
|
||||
from ast import literal_eval
|
||||
if not type(key) is str: raise TypeError('Key should be a string, found {0}'.format(type(key)))
|
||||
|
||||
entry = self.getFromWhiteBoardServer(key)
|
||||
|
||||
values = None
|
||||
if entry is not None:
|
||||
values_str = str(entry.value)
|
||||
values_str = values_str.replace('[','["').replace(']','"]'.replace(',','","'))
|
||||
values = literal_eval(values_str)
|
||||
if type(values) is not list:
|
||||
values = [values, ]
|
||||
return values
|
||||
|
||||
def getAllEntries(self):
|
||||
return self.__entries
|
||||
|
||||
def update(self):
|
||||
from requests import get, auth
|
||||
import re
|
||||
|
||||
url = CommModule.__BASE_URL + 'wbget.php'
|
||||
r = get(url, auth=auth.HTTPBasicAuth('twinPV99', 'twinPV99'))
|
||||
entries = r.text.split('\n')
|
||||
|
||||
result = {}
|
||||
for entry in entries:
|
||||
entry = entry.rstrip().lstrip()
|
||||
g = re.match('SYSLAB@(\d+):{0}::(\w+)=(.+);'.format(self.__namespace), entry)
|
||||
if g is None:
|
||||
continue
|
||||
else:
|
||||
g = g.groups()
|
||||
self.__entries[g[1]] = WhiteBoardEntry(g[1], g[2], g[0])
|
||||
|
||||
def printEntries(self):
|
||||
if len(self.__entries)==0:
|
||||
print('No WhiteBoard entries found for namespace: {0}'.format(self.__namespace))
|
||||
return
|
||||
|
||||
for e in self.__entries:
|
||||
print(e)
|
||||
|
||||
def publishList(self, key, values):
|
||||
from requests import post, auth
|
||||
if not type(key) is str: raise TypeError('Key should be a string, found {0}'.format(type(key)))
|
||||
if not type(values) is list: raise TypeError('Values should be represented by a list'.format(type(values)))
|
||||
|
||||
values_str = "[{0}]".format(",".join(map(str,values)))
|
||||
|
||||
url = '{0}{1}?source=SYSLAB&{2}::{3}={4}'.format(CommModule.__BASE_URL, 'wbset.php', self.__namespace, str(key), values_str)
|
||||
post(url, auth=auth.HTTPBasicAuth('twinPV99', 'twinPV99'))
|
||||
|
||||
def getFromWhiteBoardServer(self, key):
|
||||
"""
|
||||
Go in and read/write the value from a key from the whiteboard. The value
|
||||
should use decimal dot, not comma!
|
||||
"""
|
||||
from requests import get, auth
|
||||
import re
|
||||
|
||||
if not type(key) is str: raise TypeError('Key should be a string, found {0}'.format(type(key)))
|
||||
if key is '': raise TypeError('Key should be an empty string')
|
||||
|
||||
url = CommModule.__BASE_URL+'wbget.php'
|
||||
r = get(url, auth=auth.HTTPBasicAuth('twinPV99', 'twinPV99'))
|
||||
entries = r.text.split('\n')
|
||||
|
||||
result = None
|
||||
|
||||
for entry in entries:
|
||||
entry = entry.rstrip().lstrip()
|
||||
g = re.match('SYSLAB@(\d+):{0}::{1}=(.+);'.format(self.__namespace, key), entry)
|
||||
if g is None:
|
||||
continue
|
||||
else:
|
||||
g = g.groups()
|
||||
result = WhiteBoardEntry(key, g[1], g[0])
|
||||
break
|
||||
|
||||
return result
|
||||
|
||||
def publishToWhiteBoardServer(self, key, value):
|
||||
from requests import post, auth
|
||||
if not type(key) is str: raise TypeError('Key should be a string, found {0}'.format(type(key)))
|
||||
if type(value) is list or type(value) is dict or type(value) is tuple: raise TypeError('This function only supports single values, found {0}'.format(type(value)))
|
||||
|
||||
url = '{0}{1}?source=SYSLAB&{2}::{3}={4}'.format(CommModule.__BASE_URL, 'wbset.php', self.__namespace, str(key), str(value))
|
||||
post(url, auth=auth.HTTPBasicAuth('twinPV99', 'twinPV99'))
|
||||
|
||||
def getPoolKeys(self):
|
||||
from requests import get, auth
|
||||
import re
|
||||
|
||||
url = CommModule.__BASE_URL+'wbget.php'
|
||||
r = get(url, auth=auth.HTTPBasicAuth('twinPV99', 'twinPV99'))
|
||||
entries = r.text.split('\n')
|
||||
|
||||
result = {}
|
||||
for entry in entries:
|
||||
entry = entry.rstrip().lstrip()
|
||||
g = re.match('SYSLAB@(\d+):{0}::(\w+)=(.+)'.format(self.__namespace), entry)
|
||||
if g is None:
|
||||
continue
|
||||
else:
|
||||
g = g.groups()
|
||||
result[g[1]] = g[2]
|
||||
|
||||
return result
|
||||
|
||||
def clearKey(self, key):
|
||||
from requests import post, auth
|
||||
if not type(key) is str: raise TypeError('Key should be a string, found {0}'.format(type(key)))
|
||||
|
||||
url = '{0}{1}?source=SYSLAB&key={2}::{3}'.format(CommModule.__BASE_URL, 'wbclean.php', self.__namespace, str(key))
|
||||
post(url, auth=auth.HTTPBasicAuth('twinPV99', 'twinPV99'))
|
||||
|
||||
def clearAllKeys(self):
|
||||
entries = self.getPoolKeys()
|
||||
|
||||
for key in entries.keys():
|
||||
self.clearKey(key)
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
class WhiteBoardEntry:
|
||||
def __init__(self, key, value, time):
|
||||
self.__key = key
|
||||
self.__value = value
|
||||
self.__time = int(time)
|
||||
|
||||
@property
|
||||
def key(self):
|
||||
return self.__key
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
return self.__value
|
||||
|
||||
@property
|
||||
def time(self):
|
||||
return self.__time
|
||||
|
||||
def __str__(self):
|
||||
from datetime import datetime
|
||||
return 'WhiteBoardEntry: {0} -> {1} (@time: {2})'.format(self.key, self.value, datetime.fromtimestamp(self.time))
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
from bs4 import BeautifulSoup
|
||||
import requests
|
||||
|
||||
def getFromWhiteBoardServer(key):
|
||||
"""
|
||||
Go in and read/write the value from a key from the whiteboard. The value
|
||||
should use decimal dot, not comma!
|
||||
"""
|
||||
|
||||
url = 'http://whiteboard.syslab.dk/wbget.php?mode=html'
|
||||
r = requests.get(url, auth=requests.auth.HTTPBasicAuth('twinPV99', 'twinPV99'))
|
||||
data = r.text
|
||||
print(data)
|
||||
soup = BeautifulSoup(data)
|
||||
table = soup.find('table')
|
||||
key = table.find('td', text=key)
|
||||
value = key.findNext('td')
|
||||
return value.text
|
||||
|
||||
|
||||
def publishToWhiteBoardServer(key, value):
|
||||
url = 'http://whiteboard.syslab.dk/wbset.php?source=SYSLAB&' + key + '=' + str(value)
|
||||
r = requests.post(url, auth=requests.auth.HTTPBasicAuth('twinPV99', 'twinPV99'))
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
from .CommModule import CommModule
|
||||
from .WhiteBoardEntry import WhiteBoardEntry
|
||||
from .Whiteboard import publishToWhiteBoardServer, getFromWhiteBoardServer
|
||||
Loading…
Reference in New Issue