Initial import of Labcontrol version 2.0 from Launchpad

remotes/origin/HEAD
markuspg 8 years ago
parent 18f082272e
commit 50297fe872

3
.gitignore vendored

@ -27,3 +27,6 @@
*.exe
*.out
*.app
# Qt Creator User Project Files
*.pro.user

2403
Doxyfile

File diff suppressed because it is too large Load Diff

@ -0,0 +1,53 @@
QT += core gui network widgets
TARGET = labcontrol
TEMPLATE = app
SOURCES += src/main.cpp \
src/mainwindow.cpp \
src/sessiondisplay.cpp \
src/sessionstarter.cpp \
src/Lib/client.cpp \
src/Lib/clienthelpnotificationserver.cpp \
src/Lib/clientpinger.cpp \
src/Lib/lablib.cpp \
src/Lib/netstatagent.cpp \
src/Lib/receipts_handler.cpp \
src/Lib/receiptsprinter.cpp \
src/Lib/session.cpp \
src/Lib/sessionsmodel.cpp \
src/Lib/ztree.cpp
HEADERS += src/mainwindow.h \
src/sessiondisplay.h \
src/sessionstarter.h \
src/Lib/client.h \
src/Lib/clienthelpnotificationserver.h \
src/Lib/clientpinger.h \
src/Lib/global.h \
src/Lib/lablib.h \
src/Lib/netstatagent.h \
src/Lib/receipts_handler.h \
src/Lib/receiptsprinter.h \
src/Lib/session.h \
src/Lib/sessionsmodel.h \
src/Lib/ztree.h
FORMS += src/mainwindow.ui \
src/sessiondisplay.ui \
src/sessionstarter.ui
QMAKE_CXXFLAGS += -std=c++11
OTHER_FILES += \
labcontrol.desktop \
LICENSE \
PROGRAMMING \
README \
data/example_header.tex \
data/Labcontrol.conf \
data/scripts/kill_zLeaf_labcontrol2.sh \
data/scripts/start_zLeaf_labcontrol2.sh \
data/scripts/start_zTree_labcontrol2.sh \
doc/manual.tex

@ -0,0 +1,11 @@
# Programming
## Code Structure
The code is structered in a way that everything belonging to the gui is declared and defined in the _MainWindow_ class. All program logic is defined in the classes contained in `Lib` the main actor being the _Lablib_ class.
For example:
If the _Boot_ button on the gui is clicked, the boot function of the existing _Lablib_ instance is executed. _Lablib_'s boot function checks for all activated clients and calls the boot functions of the Client instances, which make a call to wakeonlan.
=> MainWindow::on_PBBoot_clicked() calls Lablib::boot() calls Client::boot(QString *network_broadcast_address)
For _z-Tree_ itself the logic differs a bit. A click on _Start zTree_ does not directly create a new _z-Tree_ instance. All running _z-Tree_ instances are stored in a std::list of instances of the _Session_ class containing all important informations of the running session. The constructor of these _Session_ instances starts a new _z-Tree_.

@ -0,0 +1,13 @@
# Labcontrol
## Building and Installation
The easiest way to build and install _Labcontrol_ is to open the `labcontrol.pro` file with _Qt Creator_ and build the project. Afterwards _Labcontrol_ can be started by running the created executable file. To build correctly _Qt5_ has to be available and must be know to _Qt Creator_.
To work properly _Labcontrol_ requires a file named `labcontrol.conf` in `/etc/xdg/Economic\ Laboratory`. This directory must be created, the existing dummy file be copied there and afterwards be modified according to the laboratory's needs. All _z-Tree_ executables should reside in sub-directories of `/opt` following the naming scheme `zTree_X.Y.ZZ`.
Receipt creation requires at least one receipt template in `/usr/share/labcontrol`. The name of the header file should match the pattern `NAMETHEHEADERSHALLHAVE_header.tex` to be recognized.
## Contact
If you have any questions or suggestions you can gladly contact me via my Github account _markuspg_.

@ -0,0 +1,56 @@
[General]
# The user names of all users which shall be able to conduct administrative tasks with Labcontrol
admin_users="UserA|UserB|UserC"
# The port the client help server shall listen on
client_help_server_port=XXXX
# Client settings
# The client settings are represented as an array. So the info of the first client stands in the first field in every section and the same is valid for the other clients with other indices each.
client_ips=192.168.1.1|192.168.1.2|192.168.1.3|192.168.1.4|192.168.1.5|192.168.1.6|192.168.1.7|192.168.1.8|192.168.1.9|192.168.1.10|192.168.1.11|192.168.1.12|192.168.1.13|192.168.1.14|192.168.1.15|192.168.1.16|192.168.1.17|192.168.1.18|192.168.1.19|192.168.1.20|192.168.1.21|192.168.1.22|192.168.1.23|192.168.1.24
client_macs=00:00:00:00:00:00|00:00:00:00:00:00|00:00:00:00:00:00|00:00:00:00:00:00|00:00:00:00:00:00|00:00:00:00:00:00|00:00:00:00:00:00|00:00:00:00:00:00|00:00:00:00:00:00|00:00:00:00:00:00|00:00:00:00:00:00|00:00:00:00:00:00|00:00:00:00:00:00|00:00:00:00:00:00|00:00:00:00:00:00|00:00:00:00:00:00|00:00:00:00:00:00|00:00:00:00:00:00|00:00:00:00:00:00|00:00:00:00:00:00|00:00:00:00:00:00|00:00:00:00:00:00|00:00:00:00:00:00|00:00:00:00:00:00
# These are the names which show up in the interface. They should match the hostnames of the clients
client_names=client01|client02|client03|client04|client05|client06|client07|client08|client09|client10|client11|client12|client13|client14|client15|client16|client17|client18|backupclient01|backupclient02|backupclient03|backupclient04|backupclient05|backupclient06
# This is just a hash to control the correct length of the other client attributes
client_quantity=24
# This should be set to 1, if a webcam is connected to a client
client_webcams=0|1|0|1|0|1|0|1|0|1|0|1|0|1|0|1|0|1|0|1|0|1|0|1
# The following two coordinates specify where the client will be shown in the coordinate grid at the bottom of the Labcontrol screen
client_xpos=1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24
client_ypos=1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1
# If multiple receipts are availabe, this indicates, which one will be shown by default (the index counting from 0 following an alphabetical ordering)
default_receipt_index=1
dvips_command=/usr/bin/dvips
file_manager=/usr/bin/nautilus
# The port which shall be used by zTree by default
initial_port=8000
# Where all Labcontrol data got installed
labcontrol_installation_directory=/usr/share/labcontrol
latex_command=/usr/bin/latex
# The default name for locally started zLeaves
local_zLeaf_name=local
lpr_command=/usr/bin/lpr
netstat_command=/bin/netstat
network_broadcast_address=192.168.1.255
# A combination of an installed browser and the address of your lab's ORSEE
orsee_command=firefox http://yourORSEEserver.tld
ping_command=/bin/ping
postscript_viewer=/usr/bin/okular
ps2pdf_command=/usr/bin/ps2pdf
# The public keys to access the clients
public_key_path_root=/usr/share/labcontrol/id_labclient_root
public_key_path_user=/usr/share/labcontrol/id_labclient_dsa
rcp_command=/usr/bin/rcp
rm_command=/bin/rm
# The IP of the server running zTree
server_ip=192.168.1.200
ssh_command=/usr/bin/ssh
terminal_emulator_command=/usr/bin/gnome-terminal
# The name of the user of the clients which is used to conduct experiments
user_name_on_clients=user
vnc_viewer=/usr/bin/vinagre
wakeonlan_command=/usr/bin/wakeonlan
# URLs to available webcams
webcams="webcam_left|webcam_right"
wmctrl_command=/usr/bin/wmctrl
xset_command=/usr/bin/xset
# The folder were all zTree versions are installed (in subfolders matching the scheme zTree_X.Y.Z)
ztree_installation_directory=/opt

@ -0,0 +1,51 @@
\documentclass[12pt]{article}
\usepackage[utf8]{inputenc}
\usepackage[german]{babel}
\usepackage[T1]{fontenc}
\usepackage{marvosym,pst-all}
\usepackage{datetime}
\usepackage{colortbl}
\catcode`\_=\active \global\let_=\_
\sloppy
\begin{document}
\pagestyle{empty}
\def\BB{\clearpage
\rput(0,-10.5mm){
\psset{linewidth=.3mm}
\rput(77mm,0){
\psline(-20mm,0)(81.5mm,0)
\rput(0,-1mm){\psline[linewidth=2mm](0,0)(81.5mm,0)}
\rput[t](40.05mm,6mm){\renewcommand{\rmdefault}{ppl}\rmfamily\normalsize
\begin{tabular}[t]{l}\Large The name of your organization\\[1ex]The name of your division\\
Experimental lab\end{tabular}}}}}
\newcommand{\COMPREHENSION}[2]{
%Zusammenfassungstabelle
\BB\\[3cm]\today \space --- \currenttime\\[2ex]
\begin{tabular}{lllr}
Experiment & Rechner & Name & Auszahlung \\ \hline
#1
\hline &&& #2 \EUR\end{tabular}
}
\newcommand{\GAINRECEIPT}[4]{
\BB
\vspace*{3cm}
\paragraph*{Quittung: Teilnahme an einem Laborexperiment}\mbox{}
\[\begin{tabular}{l|l|l|r}
Experiment & Rechner & Name & Auszahlung \\ \hline
#1 & #2 & #3 & #4 \EUR\end{tabular}\]Hiermit best\"{a}tige ich, dass ich den o.g. Betrag in bar ausgezahlt bekommen habe. Der Betrag wird brutto ausbezahlt. Eventuelle Steuern und sonstige Abgaben sind vom Empf\"{a}nger des Geldbetrages (Experimentteilnehmer) selbst zu entrichten.
\vspace{1cm}\begin{tabbing}\hspace{0.5\textwidth} \= \kill
Jena, \today \> \rule{0.5\textwidth}{1pt}\\ \> #3 \end{tabbing}
}
\newcommand{\LOSSRECEIPT}[4]{
\BB
\vspace*{3cm}
\paragraph*{Quittung: Teilnahme an einem Laborexperiment}\mbox{}
\[\begin{tabular}{l|l|l|r}
Experiment & Rechner & Name & Einzahlung \\ \hline
#1 & #2 & #3 & #4 \EUR\end{tabular}\]Hiermit wird best\"{a}tigt, dass die Versuchsperson im Experiment einen Verlust gemacht hat und den o.g. Betrag in bar eingezahlt hat.
\vspace{1cm}\begin{tabbing}\hspace{0.5\textwidth} \= \kill
Jena, \today \> \rule{0.5\textwidth}{1pt}\\ \> Experimentleiter \end{tabbing}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
version="1.1"
width="128"
height="128"
id="svg2">
<defs
id="defs4" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
transform="translate(0,-924.36218)"
id="layer1">
<text
x="-7.7826405"
y="532.22998"
transform="scale(0.50644392,1.9745523)"
id="text2985"
xml:space="preserve"
style="font-size:91.56048584px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#aa0000;fill-opacity:1;stroke:none;font-family:Sans"><tspan
x="-7.7826405"
y="532.22998"
id="tspan2987"
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;fill:#0000ff;font-family:Cantarell;-inkscape-font-specification:Cantarell Italic">BOOT</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
version="1.1"
width="128"
height="128"
id="svg2">
<defs
id="defs4" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
transform="translate(0,-924.36218)"
id="layer1">
<text
x="-7.2828412"
y="498.05026"
transform="scale(0.47392021,2.1100598)"
id="text2985"
xml:space="preserve"
style="font-size:85.68048859px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#aa0000;fill-opacity:1;stroke:none;font-family:Sans"><tspan
x="-7.2828412"
y="498.05026"
id="tspan2987"
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;fill:#0000ff;font-family:Cantarell;-inkscape-font-specification:Cantarell Italic">DOWN</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

@ -0,0 +1,76 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
width="128"
height="128"
id="svg2">
<defs
id="defs4">
<inkscape:path-effect
effect="envelope"
id="path-effect3038" />
<inkscape:path-effect
effect="spiro"
id="path-effect3036" />
<inkscape:path-effect
effect="bend_path"
id="path-effect3034" />
</defs>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
transform="translate(0,-924.36218)"
id="layer1">
<g
id="g3015"
style="fill:#00ff00;fill-opacity:1;fill-rule:evenodd;stroke:#00ff00;stroke-width:4;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none">
<path
d="M -5.497222e-7,1051.3707 112.63417,1052.3622 l 15.1244,-0.1385 -98.770747,-0.7708 z"
id="path3025"
style="fill:#afafde;fill-rule:evenodd;stroke:none" />
<path
d="m -5.497222e-7,980.98163 0,70.38907 28.9878235497222,0.082 0,-58.29103 z"
id="path3017"
style="fill:#353564;fill-rule:evenodd;stroke:none" />
<path
d="m 28.987823,993.16187 98.770747,-32.49623 0,91.55806 -98.770747,-0.7708 z"
id="path3027"
style="fill:#e9e9ff;fill-rule:evenodd;stroke:none" />
<path
d="M -5.497222e-7,980.98163 112.63417,926.94667 l 15.1244,33.71897 -98.770747,32.49623 z"
id="path3019"
style="fill:#4d4d9f;fill-rule:evenodd;stroke:none" />
<path
d="m 112.63417,926.94667 0,125.41553 15.1244,-0.1385 0,-91.55806 z"
id="path3023"
style="fill:#d7d7ff;fill-rule:evenodd;stroke:none" />
<path
d="M -5.497222e-7,980.98163 112.63417,926.94667 l 0,125.41553 L -5.497222e-7,1051.3707 z"
id="path3021"
style="fill:#8686bf;fill-rule:evenodd;stroke:none" />
</g>
<path
d="m 89.327187,16.925888 c -4.681,2.030027 -9.218366,5.315906 -13.504401,9.537011 -4.286093,4.221336 -8.060247,9.23777 -11.393292,14.785055 -3.33308,5.547468 -6.077896,11.610649 -8.22119,18.094785 -2.068179,6.374599 -3.277802,12.993285 -3.446041,20.121909 -0.168252,7.128712 0.610328,13.218058 2.124361,18.723965 1.588811,5.412317 3.831688,10.159577 6.708446,14.174247 2.876725,4.01469 6.283053,7.21484 10.445409,9.52578 4.162302,2.31094 8.95602,3.59662 14.185745,3.495 3.00882,-0.0585 5.720657,-0.34792 8.267363,-0.98846 2.54661,-0.64053 4.77262,-1.49456 6.625733,-2.37951 1.85301,-0.88494 3.39504,-1.65454 4.5864,-2.52938 1.19077,-0.9868 1.994,-1.83408 2.43909,-2.43437 -0.9007,-4.42594 -1.85037,-8.71233 -2.80073,-12.89587 -1.34861,1.34709 -3.45558,2.93466 -6.342404,4.69694 -2.886926,1.76231 -6.637856,2.8067 -11.280831,3.13599 -4.019368,0.28509 -7.552115,-0.44475 -10.611287,-2.16103 -2.988321,-1.72133 -5.542651,-4.19402 -7.533597,-7.28156 -1.988864,-3.179157 -3.463734,-6.945572 -4.466339,-11.248995 -0.931458,-4.314561 -1.370299,-9.014912 -1.255412,-14.387343 0.118558,-5.545634 0.880864,-10.687874 2.170064,-15.571393 1.363046,-4.993697 3.182113,-9.59575 5.49106,-13.732672 2.308894,-4.13675 5.036655,-7.726879 8.156901,-10.762849 3.120177,-3.035767 6.587728,-5.435344 10.275635,-6.877118 1.743309,-0.681399 3.397202,-1.001321 5.006108,-1.111217 1.679769,-0.24073 3.151484,-0.250131 4.445714,-0.01825 1.357888,0.20752 2.540018,0.49911 3.547388,0.851898 1.00725,0.353017 1.74122,0.682732 2.24573,1.126121 1.1562,-4.633929 2.33675,-9.372945 3.5069,-14.232619 -0.33946,-0.299039 -0.96449,-0.653482 -1.91283,-1.135669 -0.94846,-0.481914 -2.24059,-0.883422 -3.87442,-1.064532 -1.56948,-0.208297 -3.436181,-0.183128 -5.747374,0.162412 -2.244689,0.317054 -4.877921,1.098241 -7.837899,2.381722 z M 4.7600323,59.063959 c 0.258499,5.998166 0.1400644,11.59099 9.18e-5,16.921106 -0.1399726,5.330116 -0.3014832,10.397524 -0.4078143,15.413904 -0.106331,5.016379 -0.1574826,9.981731 -0.3554151,15.176771 -0.1979325,5.19504 -0.5426459,10.61978 -1.5147789,16.62397 7.0830483,0.2992 14.4483902,0.35971 21.9715462,0.37174 7.523156,0.012 15.203955,-0.0277 22.913705,-0.0699 0.525702,-3.80484 0.886229,-7.48921 1.151403,-11.09107 -5.877401,0.35414 -11.762477,0.71143 -17.593973,1.05684 -5.831496,0.34541 -11.60932,0.67983 -17.27422,0.9468 0.471746,-5.26066 0.649892,-10.22912 0.784252,-15.113272 0.13436,-4.88415 0.224935,-9.683994 0.351863,-14.567333 0.126929,-4.88334 0.290212,-9.850176 0.400313,-15.028212 0.110101,-5.178035 0.167021,-10.56727 -0.08845,-16.255306 -3.486786,1.841002 -6.9365435,3.715025 -10.3385207,5.614002 z"
transform="translate(0,924.36218)"
id="text3029"
style="font-size:40px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#00ffff;fill-opacity:1;stroke:#ffffff;stroke-width:3;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
version="1.1"
width="128"
height="128"
id="svg2">
<defs
id="defs4" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
transform="translate(0,-924.36218)"
id="layer1">
<text
x="-4.2638183"
y="660.69568"
transform="scale(0.62925446,1.5891822)"
id="text2985"
xml:space="preserve"
style="font-size:106.49146271px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#aa0000;fill-opacity:1;stroke:none;font-family:Sans"><tspan
x="-4.2638183"
y="660.69568"
id="tspan2987"
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;fill:#aa0000;font-family:Cantarell;-inkscape-font-specification:Cantarell Italic">OFF</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
version="1.1"
width="128"
height="128"
id="svg2">
<defs
id="defs4" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
transform="translate(0,-924.36218)"
id="layer1">
<text
x="-4.8633037"
y="753.58832"
transform="scale(0.71772652,1.3932884)"
id="text2985"
xml:space="preserve"
style="font-size:121.463974px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"><tspan
x="-4.8633037"
y="753.58832"
id="tspan2987"
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;fill:#008000;font-family:Cantarell;-inkscape-font-specification:Cantarell Italic">ON</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
version="1.1"
width="128"
height="128"
id="svg2">
<defs
id="defs4" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
transform="translate(0,-924.36218)"
id="layer1">
<text
x="29.27072"
y="891.0849"
transform="scale(0.84674739,1.1809898)"
id="text2985"
xml:space="preserve"
style="font-size:146.0327301px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"><tspan
x="29.27072"
y="891.0849"
id="tspan2987"
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;fill:#0000ff;font-family:Cantarell;-inkscape-font-specification:Cantarell Italic">?</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
version="1.1"
width="128"
height="128"
id="svg2">
<defs
id="defs4" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
transform="translate(0,-924.36218)"
id="layer1">
<text
x="0.25513056"
y="552.87659"
transform="scale(0.52653898,1.8991946)"
id="text2985"
xml:space="preserve"
style="font-size:87.08456421px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#aa0000;fill-opacity:1;stroke:none;font-family:Sans"><tspan
x="0.25513056"
y="552.87659"
id="tspan2987"
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;fill:#aa0000;font-family:Cantarell;-inkscape-font-specification:Cantarell Italic">zLeaf</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

@ -0,0 +1,3 @@
#!/bin/bash
/usr/bin/killall -I -q zleaf.exe

@ -0,0 +1,15 @@
#!/bin/bash
# Start a zLeaf with the given arguments
if [ $# == 2 ]; then
DISPLAY=:0.0 /usr/bin/wine /opt/zTree_$1/zleaf.exe /server $2 &
exit
fi
if [ $# == 3 ]; then
DISPLAY=:0.0 /usr/bin/wine /opt/zTree_$1/zleaf.exe /server $2 /channel $3 &
exit
fi
if [ $# == 4 ]; then
DISPLAY=:0.0 /usr/bin/wine /opt/zTree_$1/zleaf.exe /server $2 /channel $3 /name $4 &
exit
fi

@ -0,0 +1,3 @@
#!/bin/bash
/usr/bin/wine32 $1/$2/ztree.exe /datadir Z:/$3 /privdir Z:/$3 /gsfdir Z:/$3 /tempdir Z:/$3 /leafdir Z:/$3 /channel $4 &

@ -0,0 +1,126 @@
\documentclass[a4paper,10pt]{article}
\usepackage[english]{babel}
\usepackage[T1]{fontenc}
\usepackage[left=3cm, right=3cm, top=3cm, bottom=3cm]{geometry}
\usepackage{graphicx}
\usepackage[utf8]{inputenc}
%opening
\title{Labcontrol Manual}
\author{Markus Prasser}
\parindent 0pt
\parskip 12pt
\begin{document}
\maketitle
\tableofcontents
\newpage
\section{Introduction}
\emph{Labcontrol} is a tool to control a laboratory used for economic experiments with the software \emph{z-Tree}. This laboratory consists of a \emph{Linux} server and 18 \emph{Linux} clients used to conduct those experiments. The primary functions of \emph{Labcontrol} are the startup and management of experiment sessions, automated receipts printing and easy administration of the clients.
\begin{center}
\includegraphics[scale=.4]{pictures/startup_screen.png}
\end{center}
\subsection{Working Features}
\begin{itemize}
\item A \emph{Qt5} GUI for experiments and administration showing the clients' current states
\item boot and shutdown the clients, start and kill \emph{z-Leaves} on them using \emph{ssh}
\item detect all installed \emph{z-Tree} versions
\item automatic receipts printing after creation of the payment file by \emph{z-Tree}
\item beam media files to the clients using \emph{rcp}
\item watch clients' desktops using \emph{VNC}
\item open external programs showing the laboratories' webcams
\item execute admin functions on the clients
\item automatic and regular querying of the clients' statuses using \emph{ping} and \emph{netstat}
\item simultaneous running of multiple \emph{z-Tree} instances on different ports
\item administrator and experiment users with different privileges
\end{itemize}
\subsection{Wish-list}
\begin{itemize}
\item logging support for a better overview over experiments and error discovery in the laboratory
\item integration into \emph{ORSEE} for automated experiment finishing
\item improve handling of multiple concurrent sessions, currently just viewing them is supported
\end{itemize}
\section{Requirements}
Labcontrol was designed and programmed to be as error insensitive as possible. Even with all data and settings (except the executable of course) missing it should still be able to start up and run. Only the functionality will be very limited. This was made to make installing and testing as easy as possible by allowing a step by step approach.
\subsection{Files And Basic Environment}
\emph{Labcontrol} was tried to be programmed as versatile as possible, e.g. all important paths are configurable with \emph{Qt}'s QSettings configuration system. Also we tried to include no compilation time dependencies except \emph{Qt5}.
\begin{itemize}
\item Your setup should consist of a server running \emph{z-Tree} and clients to run the \emph{z-Leaves}.
\item Our laboratory works fine using \emph{Debian Jessie} with a self compiled \emph{wine} in version 1.4.1.
\item All clients should be listed in \texttt{/etc/hosts} on the server and the server on the clients. For most functions the IP addresses are used directly, but the status querying relies on hostnames.
\item All clients should have a special user dedicated for experiments. Since \emph{Labcontrol} uses \emph{ssh}, password-less public-key authentication must be available at least for the user. For administrative tasks on the client also \texttt{root} must be available for public-key authentication.
\item All \emph{z-Tree} and \emph{z-Leaf} executables should be stored in a central folder on the server and the clients with each version in a separate folder following the naming scheme \texttt{zTree\_X.Y.Z}, otherwise the automatic detection will not work.
\item The directories in the \texttt{data} directory should be copied to the so called \emph{labcontrol installation directory}, both on the server and the clients.
\item The \texttt{Labcontrol.conf} file should be copied to the place where \emph{Qt} stores the \emph{QSetting}s files (on \emph{Linux} this should be \texttt{/etc/xdg/Economic\ Laboratory}). Afterwards it should be adjusted to match the laboratory's specifications.
\item To automatically deploy media files on the clients in the client's experiment user's home directory there should be a read-write accessible directory \texttt{media4ztree}
\end{itemize}
\subsection{External Runtime Dependencies}
\begin{itemize}
\item on \emph{Linux} \emph{wine} to run the \emph{z-Tree} and \emph{z-Leaf} binaries
\item a wake-on-lan program for remote client startup
\item public-key enabled \emph{ssh} access to the clients
\item optionally a \emph{VNC} client to show the clients' desktops (and a \emph{VNC} server on them)
\item a web browser to show \emph{ORSEE}
\item \emph{latex}, \emph{dvips}, \emph{ps2pdf} and \emph{lpr} for automatic receipts creation and printing
\item a postscript viewer to display the generated receipts
\end{itemize}
\section{Compilation And Installation}
To compile Labcontrol it should be sufficient to open the \texttt{Labcontrol.pro} file with \emph{Qt Creator} and compile after choosing the desktop profile.
In the compilation process a special build directory will be created in which the compiled executable will be found after successfull compilation. You can place this executable anywhere you want. On \emph{Linux} we suggest to place it in \texttt{/usr/local/bin}. \emph{Linux} users also can use the supplied \texttt{labcontrol.desktop} file and place it in \texttt{/usr/share/applications} to have an accessible desktop icon.
Like already mentioned all \emph{z-Tree} and \emph{z-Leaf} versions should be copied to a central directory, the \emph{zTree installation directory}. This directory will be automatically scanned for installed \emph{z-Tree} versions by searching for folders matching the pattern \texttt{zTree\_X.Y.Z}.
The directories in the \texttt{data} directory should be copied to the \emph{zTree installation directory}.
Afterwards place the \texttt{Labcontrol.conf} in a subfolder \texttt{Economic Laboratory} in the place where \emph{QSettings} stores its definitions on your platform (on \emph{Linux} this would be \texttt{/etc/xdg/Economic Laboratory/Labcontrol.conf}) and adjust it to your needs and prerequisites.
To allow \emph{Labcontrol} to control the clients password-less public-key authentication must be available at least for the experiment user on the clients. For administration this is also required for \texttt{root}. Also all clients should be known by \texttt{ /etc/ssh/ssh\_known\_hosts}. This file must be readable by the users! If this is missing \emph{Labcontrol} will query the user on every connection attempt, which is not user-friendly.
\section{Using Labcontrol}
After startup \emph{Labcontrol} initializes all available components and loads its settings stored in \emph{QSettings}. On any possible error \emph{Labcontrol} will display an error message what type of error occurred and what consequences it has for the functionality of \emph{Labcontrol}.
Afterwards \emph{Labcontrol} will greet the user with its main window. This consists of a table view at the bottom showing all known clients and their current states. This view may take some seconds to initialize depending on the time it takes to query the initial client statuses. In the upper area there is a tabbed area consisting of three tabs. Most users will only use the \emph{Experimenter actions} tab which hosts all options needed to conduct experiments. Only admins are allowed to use the \emph{Admin actions} tab, which will be deactivated for all other users. The \emph{Debug messages} tab does exactly what its title says: display debug messages. It's useful for checking configuration and other errors but is of no interest for normal users. At last license and building information will be shown in the \emph{Info} tab.
\subsection{The Experimenter actions tab}
This tab consists of five group boxes grouping tasks and options of one kind. The first one is the \emph{z-Tree} one. Before you start a session you need to choose a particular z-Tree version, the port you want to use and where the data shall be stored. If this path does not exist it will be created by \emph{Labcontrol}. Since this path will be passed to \emph{z-Tree} please do not use any special characters of spaces in it. Otherwise there will be the possibility of data loss. After all settings where done \emph{z-Tree} can be started by clicking the respective button. Because of the possibility to run multiple simultaneous sessions (which likewise means \emph{z-Tree} instances) a function to display all active sessions was also implemented. It allows no modifications but just the display of currently running sessions.
To the start of a session is also bound the start of all receipts printing facilities. So users are strongly advised not to kill \emph{Labcontrol} while conducting a session because then the automated receipts printing will not work. \emph{z-Tree} is always started as a stand-alone program so killing \emph{Labcontrol} or a crash of it will mean no harm to \emph{z-Tree}.
The \emph{Client actions} and \emph{Further local options} group boxes are self explanatory.
Another important group box for the sessions is the \emph{Receipts} group box. All settings made there apply to the next started session and cannot be altered after starting a session. The user is able to choose between several installed receipt templates (if there are any installed) and can optionally make the receipts anonymous. If the user chooses to do so he is also able to choose the string which shall replace the user name. Printing receipts for local clients (\emph{z-Leaf} instances on the server) can be deactivated. This only works if the name given to the local client contains the string \texttt{local}. Also the user should take care, that none of the participants has \texttt{local} in his name, because then this participant would not get a receipt generated.
The last one is the \emph{Special actions} group box. This allows you to choose a webcam to show. After choosing a webcam an external program will open showing a window with the cameras view. Second there is a button to manually deactivate the screen savers of the clients. In some cases (e.g. replacing a client with a maintenance one) \emph{z-Leaves} can be started with others names than their own client's host name. To do this the user can choose the name of any client and afterwards click the \emph{Run z-Leaf} button. Last there is the option to beam files to an already existing directory \texttt{media4ztree} in the client user's home directory. All files which shall be beamed should be placed in one folder. The folder name and the in it contained files' names should also include no special characters of spaces.
\subsection{The Admin actions tab}
This tab allows to do some administrative tasks. The user can open terminals or file managers connected to the clients. Also specific commands can be run for the entire laboratory.
\subsection{The Debug messages tab}
This tab is very useful for installing and debugging. All configuration options get listed there and if you run a command or click a button (e.g. the "`Start z-Leaf"' button) the command behind this function is displayed there, so you can copy and paste it into a terminal and check there, why it is not working.
\end{document}

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

@ -0,0 +1,11 @@
[Desktop Entry]
Version=2.0
Name=Labcontrol
GenericName=Labcontrol
Comment=Control the EWF lab
Type=Application
Exec=/usr/local/bin/labcontrol
TryExec=/usr/local/bin/labcontrol
Terminal=false
StartupNotify=true
Icon=/usr/share/labcontrol/icons/icon.png

@ -0,0 +1,301 @@
/*
* Copyright 2014-2016 Markus Prasser
*
* This file is part of Labcontrol.
*
* Labcontrol is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Labcontrol is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Labcontrol. If not, see <http://www.gnu.org/licenses/>.
*/
#include "client.h"
lcClient::lcClient( QPlainTextEdit *argDebugMessagesTextEdit, QString *argIP, QString *argMAC, QString *argName, unsigned short int argXPosition, unsigned short int argYPosition, bool argHasWebcam, const QVector< QString* > * const argSettingsItems ):
hasWebcam{ argHasWebcam },
ip{ *argIP },
mac{ *argMAC },
name{ *argName },
xPosition{ argXPosition },
yPosition{ argYPosition },
debugMessagesTextEdit{ argDebugMessagesTextEdit },
protectedCycles{ 0 },
settingsItems{ argSettingsItems }
{
qRegisterMetaType< state_t >( "STATE" );
if ( ( *settingsItems )[ ( int )settingsItems_t::PING_COMMAND ] ) {
pinger = new lcClientPinger{ &ip, ( *settingsItems )[ ( int )settingsItems_t::PING_COMMAND ] };
pinger->moveToThread( &pingerThread );
connect( &pingerThread, &QThread::finished, pinger, &QObject::deleteLater );
connect( this, &lcClient::PingWanted, pinger, &lcClientPinger::doPing );
connect( pinger, &lcClientPinger::PingFinished, this, &lcClient::GotStatusChanged );
// connect(pinger, &ClientPinger::ping_string, this, &Client::display_ping_string);
pingerThread.start();
pingTimer = new QTimer{ this };
connect( pingTimer, &QTimer::timeout, this, &lcClient::RequestAPing ) ;
pingTimer->start( 3000 );
}
debugMessagesTextEdit->appendPlainText( tr ( "[DEBUG] Created client %1\n\tMac: %2\tIP: %3\n\tPosition: %4x%5\t\tWebcam = %6" )
.arg( name ).arg( mac ).arg( ip ).arg( QString::number( xPosition ) )
.arg( QString::number( yPosition ) ).arg( QString::number( hasWebcam ) ) );
}
lcClient::~lcClient() {
if ( pingTimer ) {
pingTimer->stop();
}
delete pingTimer;
pingerThread.quit();
pingerThread.wait();
}
void lcClient::BeamFile( const QString &argFileToBeam, const QString * const argPublickeyPathUser, const QString * const argUserNameOnClients ) {
if ( state < state_t::RESPONDING ) {
return;
}
QStringList arguments;
arguments << "-2" << "-i" << *argPublickeyPathUser << "-l" << "32768" << "-r" << argFileToBeam << QString{ *argUserNameOnClients + "@" + name + ":media4ztree" };
// Start the process
QProcess beamFileProcess;
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
beamFileProcess.setProcessEnvironment( env );
beamFileProcess.startDetached( *( *settingsItems )[ ( int )settingsItems_t::RCP_COMMAND ], arguments );
debugMessagesTextEdit->appendPlainText( "[DEBUG] " + *( *settingsItems )[ ( int )settingsItems_t::RCP_COMMAND ] + " " + arguments.join( " " ) );
}
void lcClient::Boot( const QString * const argNetworkBroadcastAddress ) {
if ( state == state_t::SHUTTING_DOWN || state == state_t::RESPONDING ) {
return;
}
QStringList arguments;
arguments << "-i" << *argNetworkBroadcastAddress << mac;
// Start the process
QProcess wakeonlanProcess;
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
wakeonlanProcess.setProcessEnvironment( env );
wakeonlanProcess.startDetached( *( *settingsItems )[ ( int )settingsItems_t::WAKEONLAN_COMMAND ], arguments );
// Output message via the debug messages tab
debugMessagesTextEdit->appendPlainText( "[DEBUG] " +
*( *settingsItems )[ ( int )settingsItems_t::WAKEONLAN_COMMAND ] +" " + arguments.join( " " ) );
pingTimer->start( 3000 );
protectedCycles = 7;
GotStatusChanged( state_t::BOOTING );
}
void lcClient::DeactiveScreensaver( const QString * const argPublickeyPathUser, const QString * const argUserNameOnClients ) {
QStringList arguments;
arguments << "-i" << *argPublickeyPathUser << QString{ *argUserNameOnClients + "@" + name }
<< *( *settingsItems )[ ( int )settingsItems_t::XSET_COMMAND ] << "-display" << ":0.0" << "dpms" << "force" << "on";
// Start the process
QProcess deactiveScreensaverProcess;
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
deactiveScreensaverProcess.setProcessEnvironment( env );
deactiveScreensaverProcess.startDetached( *( *settingsItems )[ ( int )settingsItems_t::SSH_COMMAND ], arguments );
// Output message via the debug messages tab
debugMessagesTextEdit->appendPlainText("[DEBUG] " + *( *settingsItems )[ ( int )settingsItems_t::SSH_COMMAND ] + " " + arguments.join(" "));
}
// void Client::display_ping_string(QString *ping_string) {
// debug_messages_text_edit->appendPlainText("[DEBUG] " + *ping_string);
// delete ping_string;
// }
void lcClient::GotStatusChanged( state_t argState ) {
if ( ( protectedCycles > 0 ) && ( state == state_t::BOOTING ) && ( argState != state_t::RESPONDING ) ) {
return;
}
if ( ( protectedCycles > 0 ) && ( state == state_t::SHUTTING_DOWN ) && argState != state_t::NOT_RESPONDING ) {
return;
}
state = argState;
debugMessagesTextEdit->appendPlainText( "[DEBUG] " + name + " status changed to: " + QString::number( ( int )argState ) );
}
void lcClient::KillZLeaf( const QString * const argPublickeyPathUser, const QString * const argUserNameOnClients ) {
QStringList arguments;
arguments << "-i" << *argPublickeyPathUser << QString{ *argUserNameOnClients + "@" + name }
<< QString{ *( *settingsItems )[ ( int )settingsItems_t::LABCONTROL_INSTALLATION_DIRECTORY ] + "/scripts/kill_zLeaf_labcontrol2.sh" };
// Start the process
QProcess killZLeafProcess;
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
killZLeafProcess.setProcessEnvironment( env );
killZLeafProcess.startDetached( *( *settingsItems )[ ( int )settingsItems_t::SSH_COMMAND ], arguments );
// Output message via the debug messages tab
debugMessagesTextEdit->appendPlainText( "[DEBUG] " + *( *settingsItems )[ ( int )settingsItems_t::SSH_COMMAND ] + " " + arguments.join( " " ) );
// Restart the ping_timer, because it is stopped when a zLeaf is started
pingTimer->start( 3000 );
}
void lcClient::OpenFilesystem( const QString * const argUserToBeUsed ) {
if ( state < state_t::RESPONDING ) {
return;
}
QStringList arguments = QStringList{} << QString{ "sftp://" + *argUserToBeUsed + "@" + name };
QProcess openFilesystemProcess;
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
openFilesystemProcess.setProcessEnvironment( env );
openFilesystemProcess.startDetached( *( *settingsItems )[ ( int )settingsItems_t::FILE_MANAGER ], arguments );
debugMessagesTextEdit->appendPlainText( "[DEBUG] " + *( *settingsItems )[ ( int )settingsItems_t::FILE_MANAGER ] + " " + arguments.join( " " ) );
}
void lcClient::OpenTerminal( const QString &argCommand, const bool &argOpenAsRoot, const QString * const argPublickeyPathUser, const QString * const argUserNameOnClients ) {
if ( ( *settingsItems )[ ( int )settingsItems_t::TERMINAL_EMULATOR_COMMAND ] ) {
if ( state < state_t::RESPONDING ) {
return;
}
QStringList *arguments = nullptr;
arguments = new QStringList;
if ( !argOpenAsRoot ) {
*arguments << "--title" << name << "-e" <<
QString{ *( *settingsItems )[ ( int )settingsItems_t::SSH_COMMAND ] + " -i " + *argPublickeyPathUser + " " + *argUserNameOnClients + "@" + name };
} else {
*arguments << "--title" << name << "-e" <<
QString{ *( *settingsItems )[ ( int )settingsItems_t::SSH_COMMAND ] + " -i " + *argPublickeyPathUser + " " + "root@" + name };
}
if ( ( *settingsItems )[ ( int )settingsItems_t::TERMINAL_EMULATOR_COMMAND ]->contains( "konsole" ) ) {
arguments->prepend( "--new-tab" );
arguments->prepend( "--show-tabbar" );
} else {
if ( ( *settingsItems )[ ( int )settingsItems_t::TERMINAL_EMULATOR_COMMAND ]->contains( "gnome-terminal" ) ) {
arguments->prepend( "--tab" );
}
}
if ( !argCommand.isEmpty() ) {
arguments->last().append( " '" + argCommand + "'" );
}
QProcess openTerminalProcess;
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
openTerminalProcess.setProcessEnvironment( env );
openTerminalProcess.startDetached( *( *settingsItems )[ ( int )settingsItems_t::TERMINAL_EMULATOR_COMMAND ], *arguments );
debugMessagesTextEdit->appendPlainText("[DEBUG] " +
*( *settingsItems )[ ( int )settingsItems_t::TERMINAL_EMULATOR_COMMAND ] + " " + arguments->join(" "));
delete arguments;
}
}
void lcClient::RequestAPing() {
if ( protectedCycles > 0 ) {
--protectedCycles;
}
emit PingWanted();
}
void lcClient::SetStateToZLEAF_RUNNING( QString argClientIP ) {
if ( argClientIP != ip ) {
return;
}
if ( state != state_t::ZLEAF_RUNNING ) {
pingTimer->stop();
// Inform the ClientPinger instance, that zLeaf is now running
pinger->setStateToZLEAF_RUNNING();
this->GotStatusChanged( state_t::ZLEAF_RUNNING );
debugMessagesTextEdit->appendPlainText( "[DEBUG] Client '" + name + "' got 'ZLEAF_RUNNING' signal." );
}
}
void lcClient::ShowDesktop() {
QStringList arguments;
arguments << name;
QProcess showDesktopProcess;
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
showDesktopProcess.setProcessEnvironment( env );
showDesktopProcess.startDetached( *( *settingsItems )[ ( int )settingsItems_t::VNC_VIEWER ], arguments );
// Output message via the debug messages tab
debugMessagesTextEdit->appendPlainText( "[DEBUG] " + *( *settingsItems )[ ( int )settingsItems_t::VNC_VIEWER ] + " " + arguments.join( " " ) );
}
void lcClient::Shutdown( const QString * const argPublickeyPathUser, const QString * const argUserNameOnClients ) {
if ( state == state_t::NOT_RESPONDING || state == state_t::BOOTING || state == state_t::SHUTTING_DOWN ) {
return;
}
QStringList arguments;
arguments << "-i" << *argPublickeyPathUser << QString{ *argUserNameOnClients + "@" + name } << "sudo shutdown -P now";
// Start the process
QProcess shutdownProcess;
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
shutdownProcess.setProcessEnvironment( env );
shutdownProcess.startDetached( *( *settingsItems )[ ( int )settingsItems_t::SSH_COMMAND ], arguments );
// Output message via the debug messages tab
debugMessagesTextEdit->appendPlainText( "[DEBUG] " + *( *settingsItems )[ ( int )settingsItems_t::SSH_COMMAND ] + " " + arguments.join( " " ) );
// This additional 'ping_timer' start is needed for the case that the clients are shut down without prior closing of zLeaves
pingTimer->start( 3000 );
protectedCycles = 3;
GotStatusChanged( state_t::SHUTTING_DOWN );
}
void lcClient::StartZLeaf( const QString * const argPublickeyPathUser, const QString * const argUserNameOnClients, const QString * const argZTreeVersion,
const QString * const argServerIP, unsigned short int argPort, const QString * const argFakeName ) {
if ( state < state_t::RESPONDING ) {
return;
}
// Create a QMessageBox for user interaction if there is already a zLeaf running
QMessageBox *messageBoxRunningZLeafFound = nullptr;
if ( state == state_t::ZLEAF_RUNNING ) {
messageBoxRunningZLeafFound = new QMessageBox{ QMessageBox::Warning, "Running zLeaf found",
QString{ "There is already a zLeaf running on " + name + "." }, QMessageBox::No | QMessageBox::Yes };
messageBoxRunningZLeafFound->setInformativeText( "Do you want to start a zLeaf on client " + name + " nonetheless?" );
messageBoxRunningZLeafFound->setDefaultButton( QMessageBox::No );
messageBoxRunningZLeafFound->exec();
}
if ( ( messageBoxRunningZLeafFound != nullptr && messageBoxRunningZLeafFound->clickedButton() == messageBoxRunningZLeafFound->button( QMessageBox::Yes ) ) || state != state_t::ZLEAF_RUNNING ) {
QStringList arguments;
if ( argFakeName == nullptr && argPort == 7000 ) {
arguments << "-i" << *argPublickeyPathUser << QString{ *argUserNameOnClients + "@" + name }
<< "DISPLAY=:0.0" << QString{ "/home/" + *argUserNameOnClients + "/start_zLeaf_labcontrol2.sh" } << *argZTreeVersion << *argServerIP;
} else {
if ( argFakeName == nullptr ) {
arguments << "-i" << *argPublickeyPathUser << QString{ *argUserNameOnClients + "@" + name }
<< "DISPLAY=:0.0" << QString{ "/home/" + *argUserNameOnClients + "/start_zLeaf_labcontrol2.sh" } << *argZTreeVersion << *argServerIP << QString::number( static_cast< int >( argPort ) - 7000 );
} else {
arguments << "-i" << *argPublickeyPathUser << QString{ *argUserNameOnClients + "@" + name }
<< "DISPLAY=:0.0" << QString{ "/home/" + *argUserNameOnClients + "/start_zLeaf_labcontrol2.sh" } << *argZTreeVersion << *argServerIP << QString::number( static_cast< int >( argPort ) - 7000 ) << *argFakeName ;
}
}
// Start the process
QProcess startZLeafProcess;
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
startZLeafProcess.setProcessEnvironment( env );
startZLeafProcess.startDetached( *( *settingsItems )[ ( int )settingsItems_t::SSH_COMMAND ], arguments );
// Output message via the debug messages tab
debugMessagesTextEdit->appendPlainText( "[DEBUG] " + *( *settingsItems )[ ( int )settingsItems_t::SSH_COMMAND ] + " " + arguments.join( " " ) );
}
delete messageBoxRunningZLeafFound;
}

@ -0,0 +1,148 @@
/*
* Copyright 2014-2016 Markus Prasser
*
* This file is part of Labcontrol.
*
* Labcontrol is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Labcontrol is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Labcontrol. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CLIENT_H
#define CLIENT_H
#include <QMessageBox>
#include <QPlainTextEdit>
#include <QProcess>
#include <QString>
#include <QTextStream>
#include <QThread>
#include <QTimer>
#include "global.h"
#include "clientpinger.h"
//! Class which represents the clients in the lab
/*!
This class contains elements and functions needed to represent all functions of a client.
*/
class lcClient : public QObject
{
Q_OBJECT
public slots:
//! Sets the STATE of the client to 'ZLEAF_RUNNING'
void SetStateToZLEAF_RUNNING( QString argClientIP );
public:
const bool hasWebcam = false;
const QString ip;
const QString mac;
const QString name;
const unsigned short int xPosition = 1;
const unsigned short int yPosition = 1;
//! Client's constructor
/*!
@param argDebugMessagesTextEditPtr A pointer to the debug messages text edit for verbose output
@param argIP The IP address the represented client has
@param argMAC The MAC address the represented client has
@param argName The hostname of the represented client
@param argXPosition The client's x coordinate in the lab
@param argYPosition The client's y coordinate in the lab
@param argHasWebcam If the represented client has a webcam installed
@param argSettingsItems A QVector storing many needed data QStrings
*/
lcClient( QPlainTextEdit *argDebugMessagesTextEditPtr, QString *argIP, QString *argMAC, QString *argName, unsigned short int argXPosition, unsigned short int argYPosition, bool argHasWebcam, const QVector< QString* > * const argSettingsItems );
//! Client's destructor
~lcClient();
//! Beams the chosen file to the client's 'media4ztree' directory
/*!
@param argFileToBeam The file which shall be beamed to the client's 'media4ztree' directory
@param argPublickeyPathUser The path to the publickey for user login on the clients
@param argUserNameOnClients The name of the user on the clients
*/
void BeamFile( const QString &argFileToBeam, const QString * const argPublickeyPathUser, const QString * const argUserNameOnClients );
//! Boots the client
/*!
@param argNetworkBroadcastAddress The network broadcast address of the lab
*/
void Boot( const QString * const argNetworkBroadcastAddress );
//! Runs the 'deactivate_screensaver.sh' script on the client to deactivate the screensaver
/*!
@param argPublickeyPathUser The path to the publickey for user login on the clients
@param argUserNameOnClients The name of the user on the clients
*/
void DeactiveScreensaver( const QString * const argPublickeyPathUser, const QString * const argUserNameOnClients );
//! Returns the current state of the client
/*!
@return The current state of the client
*/
state_t GetClientState() const { return state; }
//! Kills all processes 'zleaf.exe' on the client
/*!
@param argPublickeyPathUser The path to the publickey for user login on the clients
@param argUserNameOnClients The name of the user on the clients
*/
void KillZLeaf( const QString * const argPublickeyPathUser, const QString * const argUserNameOnClients );
//! Opens a file manager for the client's file system
/*!
@param argUserToBeUsed The name of the user on the clients
*/
void OpenFilesystem( const QString * const argUserToBeUsed );
//! Opens a terminal for the client
/*!
@param argCommand A command which shall be executed in the terminal window (Pass an empty QString if not wanted)
@param argOpenAsRoot Run the terminal session as root (true) or as normal user (false)
@param argPublickeyPathUser The path to the publickey for user login on the clients
@param argUserNameOnClients The name of the user on the clients
*/
void OpenTerminal( const QString &argCommand, const bool &argOpenAsRoot, const QString * const argPublickeyPathUser, const QString * const argUserNameOnClients );
//! Shows the desktop of the given client
void ShowDesktop();
//! Shuts down the client
/*!
@param argPublickeyPathUser The path to the publickey for user login on the clients
@param argUserNameOnClients The name of the user on the clients
*/
void Shutdown( const QString * const argPublickeyPathUser, const QString * const argUserNameOnClients );
//! Starts a zLeaf instance on the client
/*!
@param argPublickeyPathUser The path to the publickey for user login on the clients
@param argUserNameOnClients The name of the user on the clients
@param argZTreeVersion The version of zLeaf which shall be started
@param argServerIP The ip of the server which is running zTree
@param argPort The port zTree ist listening on on the server
@param argFakeName The name the zLeaf instance shall have (if not the default, which is the hostname of the client)
*/
void StartZLeaf( const QString * const argPublickeyPathUser, const QString * const argUserNameOnClients, const QString * const argZTreeVersion,
const QString * const argServerIP, unsigned short int argPort, const QString * const argFakeName = nullptr );
private:
QPlainTextEdit * const debugMessagesTextEdit = nullptr;
unsigned short int protectedCycles;
lcClientPinger *pinger = nullptr;
QThread pingerThread;
const QVector<QString*> * const settingsItems;
state_t state = state_t::UNINITIALIZED;
QTimer *pingTimer = nullptr; //! QTimer used to trigger pings by pinger's ClientPinger instance
private slots:
// void display_ping_string(QString *ping_string);
void GotStatusChanged( state_t argState );
void RequestAPing();
signals:
void PingWanted();
};
#endif // CLIENT_H

@ -0,0 +1,110 @@
/*
* Copyright 2014-2016 Markus Prasser
*
* This file is part of Labcontrol.
*
* Labcontrol is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Labcontrol is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Labcontrol. If not, see <http://www.gnu.org/licenses/>.
*/
#include "clienthelpnotificationserver.h"
lcClientHelpNotificationServer::lcClientHelpNotificationServer( const QMap< QString, lcClient* > * const argClientIPsToClientsMap,
const QString * const argServerIP, const unsigned short int &argServerPort, QObject *argParent ) :
QObject{ argParent },
clientIPsToClientsMap{ argClientIPsToClientsMap },
hostAddress{ *argServerIP },
serverPort{ argServerPort }
{
QNetworkConfigurationManager manager;
if ( manager.capabilities() & QNetworkConfigurationManager::NetworkSessionRequired ) {
// Get saved network configuration
QSettings settings{ QSettings::UserScope, QLatin1String{ "QtProject" } };
settings.beginGroup( QLatin1String{ "QtNetwork" } );
const QString id = settings.value( QLatin1String{ "DefaultNetworkConfiguration" } ).toString();
settings.endGroup();
// If the saved network configuration is not currently discovered use the system default
QNetworkConfiguration config = manager.configurationFromIdentifier( id );
if ( ( config.state() & QNetworkConfiguration::Discovered ) != QNetworkConfiguration::Discovered ) {
config = manager.defaultConfiguration();
}
networkSession = new QNetworkSession{ config, this };
connect( networkSession, &QNetworkSession::opened, this, &lcClientHelpNotificationServer::OpenSession );
networkSession->open();
} else {
OpenSession();
}
connect( helpMessageServer, &QTcpServer::newConnection, this, &lcClientHelpNotificationServer::SendReply );
}
void lcClientHelpNotificationServer::OpenSession() {
// Save the used configuration
if ( networkSession ) {
QNetworkConfiguration config = networkSession->configuration();
QString id;
if ( config.type() == QNetworkConfiguration::UserChoice ) {
id = networkSession->sessionProperty( QLatin1String{ "UserChoiceConfiguration" } ).toString();
} else {
id = config.identifier();
}
QSettings settings( QSettings::UserScope, QLatin1String{ "QtProject" } );
settings.beginGroup( QLatin1String{ "QtNetwork" } );
settings.setValue( QLatin1String{ "DefaultNetworkConfiguration" }, id );
settings.endGroup();
}
helpMessageServer = new QTcpServer{ this };
if ( !helpMessageServer->listen( hostAddress, serverPort ) ) {
QMessageBox messageBox{ QMessageBox::Critical, tr( "Unable to start the client help notification server" ),
tr( "Unable to start the client help notification server.\nThe following error occurred:\n\n%1." ).arg( helpMessageServer->errorString() ), QMessageBox::Ok };
messageBox.exec();
return;
}
}
void lcClientHelpNotificationServer::SendReply() {
QByteArray block;
QDataStream out{ &block, QIODevice::WriteOnly };
out.setVersion( QDataStream::Qt_5_2 );
out << ( quint16 )0;
out << QString{ "Help demand retrieved." };
out.device()->seek( 0 );
out << ( quint16 )( block.size() - sizeof( quint16 ) );
QTcpSocket *clientConnection = helpMessageServer->nextPendingConnection();
QString peerAddress = clientConnection->peerAddress().toString();
QString peerName;
bool unknownClient = false;
if ( clientIPsToClientsMap->contains( peerAddress ) ) {
peerName = ( *clientIPsToClientsMap )[ peerAddress ]->name;
} else {
unknownClient = true;
}
connect( clientConnection, &QTcpSocket::disconnected, clientConnection, &QTcpSocket::deleteLater );
clientConnection->write( block );
clientConnection->disconnectFromHost();
if ( unknownClient ) {
QMessageBox requestReceivedBox{ QMessageBox::Information, tr( "Unknown client asked for help."),
tr( "An unknown client with IP '%1' asked for help.").arg( peerAddress ), QMessageBox::Ok };
requestReceivedBox.exec();
} else {
QMessageBox requestReceivedBox{ QMessageBox::Information, tr( "'%1' asked for help.").arg( peerName ),
tr( "'%1' asked for help.").arg( peerName ), QMessageBox::Ok };
requestReceivedBox.exec();
}
}

@ -0,0 +1,53 @@
/*
* Copyright 2014-2016 Markus Prasser
*
* This file is part of Labcontrol.
*
* Labcontrol is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Labcontrol is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Labcontrol. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CLIENTHELPNOTIFICATIONSERVER_H
#define CLIENTHELPNOTIFICATIONSERVER_H
#include "src/Lib/client.h"
#include <QObject>
#include <QHostAddress>
#include <QMessageBox>
#include <QtNetwork>
class lcClientHelpNotificationServer : public QObject
{
Q_OBJECT
public:
explicit lcClientHelpNotificationServer( const QMap< QString, lcClient* > * const argClientIPsToClientsMap,
const QString * const argServerIP, const unsigned short int &argServerPort, QObject *argParent = nullptr );
signals:
public slots:
private:
const QMap< QString, lcClient* > * const clientIPsToClientsMap = nullptr;
QTcpServer *helpMessageServer = nullptr;
const QHostAddress hostAddress;
QNetworkSession *networkSession = nullptr;
const unsigned short int serverPort = 0;
private slots:
void OpenSession();
void SendReply();
};
#endif // CLIENTHELPNOTIFICATIONSERVER_H

@ -0,0 +1,65 @@
/*
* Copyright 2014-2016 Markus Prasser
*
* This file is part of Labcontrol.
*
* Labcontrol is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Labcontrol is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Labcontrol. If not, see <http://www.gnu.org/licenses/>.
*/
#include "clientpinger.h"
lcClientPinger::lcClientPinger( const QString * const argIP, const QString * const argPingCommand, QObject *argParent ) :
QObject{ argParent },
ip{ argIP },
// Arguments: -c 1 (send 1 ECHO_REQUEST packet) -w 1 (timeout after 1 second) -q (quiet output)
pingArguments{ new QStringList{ QStringList{} << "-c" << "1" << "-w" << "1" << "-q" << *ip } },
pingCommand{ argPingCommand },
pingProcess{ new QProcess{ this } },
state{ state_t::UNINITIALIZED }
{
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
pingProcess->setProcessEnvironment(env);
// emit ping_string(new QString(*ping_command + " " + ping_arguments->join(" ")));
}
lcClientPinger::~lcClientPinger() {
delete pingProcess;
delete pingArguments;
}
void lcClientPinger::doPing() {
// Initialize the new state to be queried
state_t newState = state_t::UNINITIALIZED;
// Query the current state of the client
pingProcess->start( *pingCommand, *pingArguments );
if ( !pingProcess->waitForFinished( 2500 ) )
newState = state_t::ERROR;
else {
if ( pingProcess->exitCode() == 0 )
newState = state_t::RESPONDING;
else
newState = state_t::NOT_RESPONDING;
}
if ( newState != state ) {
state = newState;
// emit ping_string(new QString(name + *ping_command + " " + ping_arguments->join(" ")));
emit PingFinished( newState );
}
}
void lcClientPinger::setStateToZLEAF_RUNNING() {
state = state_t::ZLEAF_RUNNING;
}

@ -0,0 +1,67 @@
/*
* Copyright 2014-2016 Markus Prasser
*
* This file is part of Labcontrol.
*
* Labcontrol is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Labcontrol is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Labcontrol. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CLIENTPINGER_H
#define CLIENTPINGER_H
#include <QObject>
#include <QProcess>
#include <QThread>
#include "global.h"
//! The ClientPinger class is used to do repetitive pings of the owning Client instance's client.
/*!
This class is just used for executing repetitive pings.
*/
class lcClientPinger : public QObject
{
Q_OBJECT
public:
//! ClientPinger's constructor
/*!
@param argIP A reference to the owning Client instance's IP address
@param argPingCommand The path were the command to be executed for pings resides
@param argParent The ClientPinger's parent owning this instance of it
*/
explicit lcClientPinger( const QString * const argIP, const QString * const argPingCommand, QObject *argParent = nullptr );
//! ClientPinger's destructor
~lcClientPinger();
public slots:
//! This slot executes a ping when called.
void doPing();
//! Retrieves the information, that zLeaf is running (otherwise restarting will not result in status updates)
void setStateToZLEAF_RUNNING();
private:
const QString * const ip; //! Stores a pointer to the to be pinged client's ip
const QStringList * const pingArguments; //! The arguments for the 'ping' command
const QString * const pingCommand; //! The 'ping' command itself
QProcess * pingProcess; //! The 'ping' process which will be executed on every call of 'do_ping()'
state_t state; //! Stores the current state of the client
signals:
//! This signal was just implemented for testing purposes
// void ping_string(QString *string);
//! This signal is emitted if the ping finished and the state of the client changed
void PingFinished( state_t state );
};
#endif // CLIENTPINGER_H

@ -0,0 +1,48 @@
/*
* Copyright 2014-2016 Markus Prasser
*
* This file is part of Labcontrol.
*
* Labcontrol is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Labcontrol is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Labcontrol. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef GLOBAL_H
#define GLOBAL_H
#include <QMetaType>
enum class settingsItems_t : unsigned short int { DVIPS_COMMAND, FILE_MANAGER, LABCONTROL_INSTALLATION_DIRECTORY, LATEX_COMMAND, LOCAL_ZLEAF_NAME, LPR_COMMAND,
NETSTAT_COMMAND, NETWORK_BROADCAST_ADDRESS, ORSEE_COMMAND, PING_COMMAND, POSTSCRIPT_VIEWER, PS2PDF_COMMAND,
PUBLICKEY_PATH_ROOT, PUBLICKEY_PATH_USER, RCP_COMMAND, RM_COMMAND, SERVER_IP, SSH_COMMAND, TERMINAL_EMULATOR_COMMAND,
USER_NAME_ON_CLIENTS, VNC_VIEWER, WAKEONLAN_COMMAND, WMCTRL_COMMAND, XSET_COMMAND, ZTREE_INSTALLATION_DIRECTORY, SETTINGS_ITEMS_QUANTITY };
//! Opens a terminal for the client
enum class state_t : unsigned short int {
//! The client is booting but not yet responding
BOOTING,
//! An error occurred determining the client's state
ERROR,
//! The client is not responding to pings
NOT_RESPONDING,
//! The client is shutting down but not yet stopped responding
SHUTTING_DOWN,
//! The client's state is not yet defined (should only occur directly after client creation)
UNINITIALIZED,
//! The client is responding to pings
RESPONDING,
//! The client is running a zLeaf
ZLEAF_RUNNING};
Q_DECLARE_METATYPE( state_t )
#endif // GLOBAL_H

@ -0,0 +1,467 @@
/*
* Copyright 2014-2016 Markus Prasser
*
* This file is part of Labcontrol.
*
* Labcontrol is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Labcontrol is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Labcontrol. If not, see <http://www.gnu.org/licenses/>.
*/
#include <QErrorMessage>
#include <QFile>
#include <QTextStream>
#include "lablib.h"
lcLablib::lcLablib( QPlainTextEdit *argDebugMessagesTextEdit, QObject *argParent ) :
QObject{ argParent },
clientIPsToClientsMap{ new QMap< QString, lcClient* > },
debugMessagesTextEdit{ argDebugMessagesTextEdit },
labSettings{ "Economic Laboratory", "Labcontrol", this },
occupiedPorts{ new QVector< int > },
sessionsModel{ new SessionsModel{ this } },
settingsItems{ new QVector< QString* >{ ( int )settingsItems_t::SETTINGS_ITEMS_QUANTITY, nullptr } }
{
ReadSettings();
DetectInstalledZTreeVersionsAndLaTeXHeaders();
// Initialize all 'netstat' query mechanisms
if ( ( *settingsItems )[ ( int )settingsItems_t::NETSTAT_COMMAND ] ) {
netstatAgent = new lcNetstatAgent{ ( *settingsItems )[ ( int )settingsItems_t::NETSTAT_COMMAND ] };
netstatAgent->moveToThread( &netstatThread );
connect( &netstatThread, &QThread::finished, netstatAgent, &QObject::deleteLater );
connect( netstatAgent, &lcNetstatAgent::QueryFinished, this, &lcLablib::GotNetstatQueryResult );
netstatThread.start();
netstatTimer = new QTimer{ this };
connect( netstatTimer, &QTimer::timeout, netstatAgent, &lcNetstatAgent::QueryClientConnections );
netstatTimer->start( 500 );
}
// Initialize the server for client help requests retrieval
if ( clientHelpNotificationServerPort && ( *settingsItems )[ ( int )settingsItems_t::SERVER_IP ] ) {
clientHelpNotificationServer = new lcClientHelpNotificationServer{ clientIPsToClientsMap, ( *settingsItems )[ ( int )settingsItems_t::SERVER_IP ], clientHelpNotificationServerPort, this };
}
}
lcLablib::~lcLablib () {
if ( netstatTimer ) {
netstatTimer->stop();
delete netstatTimer;
}
netstatThread.quit();
netstatThread.wait();
delete adminUsers;
if ( clients ) {
for ( QVector< lcClient* >::iterator it = clients->begin(); it != clients->end(); ++it ) {
delete *it;
}
}
delete clients;
delete InstalledZTreeVersions;
delete occupiedPorts;
delete webcams;
for (auto s: *settingsItems) {
delete s;
}
delete settingsItems;
}
bool lcLablib::CheckPathAndComplain( const QString * const argPath, const QString &argVariableName, const QString &argComplaint ) {
if ( !QFile::exists( *argPath ) ) {
QMessageBox::information( nullptr, tr( "Specified path '%1' does not exist" ).arg( argVariableName ), tr( "The path specified by '%1' does not exist. %2" ).arg( argVariableName ).arg( argComplaint ) );
debugMessagesTextEdit->appendPlainText( tr( "[DEBUG] The path specified by '%1' does not exist. %2" ).arg( argVariableName ).arg( argComplaint ) );
return false;
}
debugMessagesTextEdit->appendPlainText( tr( "[DEBUG] '%1': %2" ).arg( argVariableName ).arg( *argPath ) );
return true;
}
void lcLablib::DetectInstalledZTreeVersionsAndLaTeXHeaders() {
// Detect the installed LaTeX headers
if ( ( *settingsItems )[ ( int )settingsItems_t::LABCONTROL_INSTALLATION_DIRECTORY ] ) {
QDir laTeXDirectory{ *( *settingsItems )[ ( int )settingsItems_t::LABCONTROL_INSTALLATION_DIRECTORY ], "*header.tex", QDir::Name, QDir::CaseSensitive | QDir::Files | QDir::Readable };
if ( !laTeXDirectory.exists() || laTeXDirectory.entryList().isEmpty() ) {
QMessageBox messageBox{ QMessageBox::Critical, tr( "No LaTeX headers found" ),
tr( "No LaTeX headers could be found in '%1'. Receipts printing will not work" )
.arg( *( *settingsItems )[ ( int )settingsItems_t::LABCONTROL_INSTALLATION_DIRECTORY ] ), QMessageBox::Ok };
messageBox.exec();
installedLaTeXHeaders = new QStringList{ "None found" };
debugMessagesTextEdit->appendPlainText( tr( "[DEBUG] No LaTeX headers could be found in '%1'." )
.arg( *( *settingsItems )[ ( int )settingsItems_t::LABCONTROL_INSTALLATION_DIRECTORY ] ) );
} else {
installedLaTeXHeaders = new QStringList{ laTeXDirectory.entryList() };
installedLaTeXHeaders->replaceInStrings( "_header.tex", "" );
debugMessagesTextEdit->appendPlainText( tr( "[DEBUG] LaTeX headers: %1" ).arg( installedLaTeXHeaders->join( " / " ) ) );
}
}
// Detect the installed zTree versions
if ( ( *settingsItems )[ ( int )settingsItems_t::ZTREE_INSTALLATION_DIRECTORY ] ) {
QDir zTreeDirectory{ *( *settingsItems )[ ( int )settingsItems_t::ZTREE_INSTALLATION_DIRECTORY ], "zTree*", QDir::Name, QDir::NoDotAndDotDot | QDir::Dirs | QDir::Readable | QDir::CaseSensitive };
if ( zTreeDirectory.entryList().isEmpty() ) {
QMessageBox messageBox{ QMessageBox::Critical, tr( "zTree not found" ),
tr( "No zTree installation found in '%1'. Running zTree will not be possible." )
.arg( *( *settingsItems )[ ( int )settingsItems_t::ZTREE_INSTALLATION_DIRECTORY ] ), QMessageBox::Ok };
messageBox.exec();
debugMessagesTextEdit->appendPlainText( tr( "[DEBUG] No zTree versions could be found in '%1'." ).arg( *( *settingsItems )[ ( int )settingsItems_t::ZTREE_INSTALLATION_DIRECTORY ] ) );
}
else {
InstalledZTreeVersions = new QStringList{ zTreeDirectory.entryList() };
InstalledZTreeVersions->replaceInStrings( "zTree_", "" );
debugMessagesTextEdit->appendPlainText( tr( "[DEBUG] zTree versions: %1" ).arg( InstalledZTreeVersions->join( " / " ) ) ) ;
}
}
}
void lcLablib::GotNetstatQueryResult( QStringList *argActiveZLeafConnections ) {
if ( argActiveZLeafConnections != nullptr ) {
for ( auto s: *argActiveZLeafConnections ) {
// Set all given clients' statuses to 'ZLEAF_RUNNING'
emit ZLEAF_RUNNING( s );
}
}
else
debugMessagesTextEdit->appendPlainText( tr( "[DEBUG] Netstat status query failed." ) );
delete argActiveZLeafConnections;
}
void lcLablib::ReadSettings() {
QStringList simpleLoadableItems = { QStringList{}
<< "dvips_command"
<< "file_manager"
<< "labcontrol_installation_directory"
<< "latex_command"
<< "local_zLeaf_name"
<< "lpr_command"
<< "netstat_command"
<< "network_broadcast_address"
<< "orsee_command"
<< "ping_command"
<< "postscript_viewer"
<< "ps2pdf_command"
<< "public_key_path_root"
<< "public_key_path_user"
<< "rcp_command"
<< "rm_command"
<< "server_ip"
<< "ssh_command"
<< "terminal_emulator_command"
<< "user_name_on_clients"
<< "vnc_viewer"
<< "wakeonlan_command"
<< "wmctrl_command"
<< "xset_command"
<< "ztree_installation_directory" };
QStringList theItemsErrorComplaints = { QStringList{}
<< "Receipts creation will not work."
<< "The display of preprints will not work."
<< "Labcontrol will missbehave with high propability."
<< "Receipts creation will not work."
<< "The local zLeaf default name will default to 'local'."
<< "Receipts printing will not work."
<< "Detection of active zLeaf connections will not work."
<< "Booting the clients will not work."
<< "Opening ORSEE in a browser will not work."
<< "Status updates for the clients will not work."
<< "Viewing the generated receipts postscript file will not work."
<< "Converting and viewing the generated receipts file will not work."
<< "Administration actions concerning the clients will not be available."
<< "Many actions concerning the clients will not be available."
<< "Beaming files to the clients will not be possible."
<< "Cleanup of the zTree data target path will not work."
<< "Starting zLeaves and retrieving client help messages will not work."
<< "All actions concerning the clients will not be possible."
<< "Conducting administrative tasks will not be possible."
<< "All actions concerning the clients performed by the experiment user will not work."
<< "Viewing the client's screens will not work."
<< "Booting the clients will not work."
<< "Setting zTree's window title to its port number will not work."
<< "Deactivating the screen saver on the clients will not be possible."
<< "zTree will not be available" };
bool is_file[] = { true, true, true, true, false, true, true, false, false, true, true, true, true,
true, true, true, false, true, true, false, true, true, true, false, true };
QString *tempItemStorage = nullptr;
for ( int i = 0; i < ( int )settingsItems_t::SETTINGS_ITEMS_QUANTITY; i++ ) {
tempItemStorage = ReadSettingsItem( simpleLoadableItems[ i ], theItemsErrorComplaints[ i ], is_file[ i ] );
settingsItems->replace( i, tempItemStorage );
}
tempItemStorage = nullptr;
// Let the local zLeaf name default to 'local' if none was given in the settings
if ( !( *settingsItems )[ ( int )settingsItems_t::LOCAL_ZLEAF_NAME ] ) {
settingsItems->replace( ( int )settingsItems_t::LOCAL_ZLEAF_NAME, new QString{ tr( "local" ) } );
}
// Read the list of users with administrative rights
if ( !labSettings.contains( "admin_users" ) ) {
QMessageBox messageBox{ QMessageBox::Information, tr( "'admin_users' not set" ),
tr( "The 'admin_users' variable was not set. No users will be able to conduct administrative tasks." ) };
messageBox.exec();
debugMessagesTextEdit->appendPlainText( tr( "[DEBUG] 'admin_users' was not set. No permission for administrative tasks." ) );
} else {
adminUsers = new QStringList{ labSettings.value( "admin_users", "" ).toString().split( '|', QString::SkipEmptyParts, Qt::CaseInsensitive ) };
debugMessagesTextEdit->appendPlainText( tr( "[DEBUG] 'admin_users': %1").arg( adminUsers->join(" / ") ) );
}
// Read the port the ClientHelpNotificationServer shall listen on
clientHelpNotificationServerPort = labSettings.value( "client_help_server_port", 0 ).toUInt();
if ( !clientHelpNotificationServerPort ) {
QMessageBox messageBox{ QMessageBox::Information, tr( "The ClientHelpNotificationServer will be deactivated" ),
tr( "The 'client_help_server_port' variable was not set or set to zero. The ClientHelpNotificationServer will be deactivated. Clients' help requests will be ignored by the server." ) };
messageBox.exec();
debugMessagesTextEdit->appendPlainText( tr( "[DEBUG] The ClientHelpNotificationServer will be deactivated since 'client_help_server_port' was not set or set to zero." ) );
} else {
debugMessagesTextEdit->appendPlainText( tr( "[DEBUG] 'client_help_server_port': %1" ).arg( QString::number( clientHelpNotificationServerPort ) ) );
}
// Read the default receipt index for the 'CBReceipts' combobox
if ( !labSettings.contains( "default_receipt_index" ) ) {
QMessageBox messageBox(QMessageBox::Information, tr( "'default_receipt_index' not set" ), tr( "The 'default_receipt_index' variable was not set. It will default to '0'." ) );
messageBox.exec();
debugMessagesTextEdit->appendPlainText( tr( "[DEBUG] 'default_receipt_index' was not set. It will default to '0'." ) );
}
defaultReceiptIndex = labSettings.value( "default_receipt_index", 0 ).toInt();
debugMessagesTextEdit->appendPlainText( tr( "[DEBUG] 'default_receipt_index': %1").arg( QString::number( defaultReceiptIndex ) ) );
// Read the initial port number
if ( !labSettings.contains( "initial_port" ) ) {
QMessageBox messageBox{ QMessageBox::Information, tr( "'initial_port' not set" ),
tr( "The 'initial_port' variable was not set. Labcontrol will default to port 7000 for new zTree instances." ) };
messageBox.exec();
debugMessagesTextEdit->appendPlainText( tr( "[DEBUG] 'initial_port' was not set. Labcontrol will default to port 7000 for new zTree instances." ) );
}
chosenZTreePort = labSettings.value( "initial_port", 7000 ).toInt();
debugMessagesTextEdit->appendPlainText( tr("[DEBUG] 'initial_port': %1" ).arg( QString::number( chosenZTreePort ) ) );
// Get a list of available webcams in the lab
if ( !labSettings.contains( "webcams" ) ) {
QMessageBox messageBox{ QMessageBox::Information, tr( "'webcams' not set" ), tr( "The 'webcams' variable was not set. No stationary webcams will be available." ) };
messageBox.exec();
debugMessagesTextEdit->appendPlainText( tr( "[DEBUG] 'webcams' was not set. No stationary webcams will be available." ) );
}
else {
webcams = new QStringList{ labSettings.value( "webcams" ).toString().split( '|', QString::SkipEmptyParts, Qt::CaseInsensitive ) };
debugMessagesTextEdit->appendPlainText( tr( "[DEBUG] 'webcams': %1" ).arg( webcams->join( " / " ) ) );
}
// Get the client quantity to check the value lists for clients creation for correct length
int clientQuantity = 0;
if ( !labSettings.contains("client_quantity" ) ) {
QMessageBox messageBox{ QMessageBox::Information, tr( "'client_quantity' not set" ),
tr( "The 'client_quantity' variable was not set. The client quantity will be guessed by the amount of client ips set in 'client_ips'." ) };
messageBox.exec();
debugMessagesTextEdit->appendPlainText( tr( "[DEBUG] 'client_quantity' was not set. The client quantity will be guessed by the amount of client IPs set in 'client_ips'." ) );
clientQuantity = labSettings.value( "client_ips", "" ).toString().split( '/', QString::SkipEmptyParts, Qt::CaseSensitive ).length();
debugMessagesTextEdit->appendPlainText( tr( "[DEBUG] 'client_quantity': %1" ).arg( QString::number( clientQuantity ) ) );
} else {
clientQuantity = labSettings.value( "client_quantity" ).toInt();
debugMessagesTextEdit->appendPlainText( tr( "[DEBUG] 'client_quantity': %1" ).arg( QString::number( clientQuantity ) ) );
}
// Create all the clients in the lab
QStringList clientIPs = labSettings.value( "client_ips" ).toString().split( '|', QString::SkipEmptyParts, Qt::CaseSensitive );
if ( clientIPs.length() != clientQuantity ) {
QMessageBox messageBox{ QMessageBox::Critical, tr( "Wrong client ip quantity" ),
tr( "The quantity of client ips does not match the client quantity. Client creation will fail. No clients will be available for interaction." ), QMessageBox::Ok };
messageBox.exec();
return;
}
debugMessagesTextEdit->appendPlainText( tr( "[DEBUG] Client IPs: %1").arg( clientIPs.join( " / " ) ) );
QStringList clientMACs = labSettings.value( "client_macs" ).toString().split( '|', QString::SkipEmptyParts, Qt::CaseSensitive );
if ( clientMACs.length() != clientQuantity ) {
QMessageBox messageBox{ QMessageBox::Critical, tr( "Wrong client mac quantity" ),
tr( "The quantity of client macs does not match the client quantity. Client creation will fail. No clients will be available for interaction." ), QMessageBox::Ok };
messageBox.exec();
return;
}
debugMessagesTextEdit->appendPlainText( tr( "[DEBUG] Client MACs: %1").arg( clientMACs.join( " / " ) ) );
QStringList clientNames = labSettings.value( "client_names" ).toString().split( '|', QString::SkipEmptyParts, Qt::CaseSensitive );
if ( clientNames.length() != clientQuantity ) {
QMessageBox messageBox{ QMessageBox::Critical, tr( "Wrong client name quantity" ),
tr( "The quantity of client names does not match the client quantity. Client creation will fail. No clients will be available for interaction." ), QMessageBox::Ok };
messageBox.exec();
return;
}
debugMessagesTextEdit->appendPlainText( tr( "[DEBUG] Client names: %1" ).arg( clientNames.join( " / " ) ) );
QStringList clientWebcams = labSettings.value( "client_webcams" ).toString().split( '|', QString::SkipEmptyParts, Qt::CaseSensitive );
if ( clientWebcams.length() != clientQuantity ) {
QMessageBox messageBox{ QMessageBox::Critical, tr( "Wrong client webcam property quantity" ),
tr( "The quantity of client webcam properties does not match the client quantity. Client creation will fail. No clients will be available for interaction." ), QMessageBox::Ok };
messageBox.exec();
return;
}
debugMessagesTextEdit->appendPlainText( tr( "[DEBUG] Client webcams: %1").arg( clientWebcams.join( " / " ) ) );
QStringList clientXPositions = labSettings.value( "client_xpos" ).toString().split( '|', QString::SkipEmptyParts, Qt::CaseSensitive );
if ( clientXPositions.length() != clientQuantity ) {
QMessageBox messageBox{ QMessageBox::Critical, tr( "Wrong client x positions quantity" ),
tr( "The quantity of client x positions does not match the client quantity. Client creation will fail. No clients will be available for interaction." ), QMessageBox::Ok };
messageBox.exec();
return;
}
debugMessagesTextEdit->appendPlainText( tr( "[DEBUG] client_xpos: %1" ).arg( clientXPositions.join( " / " ) ) );
QStringList clientYPositions = labSettings.value( "client_ypos" ).toString().split( '|', QString::SkipEmptyParts, Qt::CaseSensitive );
if ( clientYPositions.length() != clientQuantity ) {
QMessageBox messageBox{ QMessageBox::Critical, tr( "Wrong client y positions quantity" ),
tr( "The quantity of client y positions does not match the client quantity. Client creation will fail. No clients will be available for interaction." ), QMessageBox::Ok };
messageBox.exec();
return;
}
debugMessagesTextEdit->appendPlainText( tr( "[DEBUG] client_ypos: %1" ).arg( clientYPositions.join( " / " ) ) );
clients = new QVector< lcClient* >;
for ( int i = 0; i < clientQuantity; i++ ) {
clients->append( new lcClient{ debugMessagesTextEdit, &clientIPs[ i ], &clientMACs[ i ], &clientNames[ i ], clientXPositions[ i ].toUShort(),
clientYPositions[ i ].toUShort(), clientWebcams[ i ].toInt(), settingsItems } );
// Add an corresponding entry to the 'client_ips_to_clients_map' std::map<QString, Client*>
( *clientIPsToClientsMap )[ clients->last()->ip ] = clients->last();
// Get the address of the Client instance in RAM for display
const void *clientPointerAddress = static_cast< const void* >( ( *clientIPsToClientsMap )[ clients->last()->ip ] );
QString clientPointerAddressString;
QTextStream clientPointerAddressStream ( &clientPointerAddressString );
clientPointerAddressStream << clientPointerAddress;
// Connect the 'Client' instance to the 'ZLEAF_RUNNING(QString client_ip)' signal
connect( this, &lcLablib::ZLEAF_RUNNING, clients->last(), &lcClient::SetStateToZLEAF_RUNNING );
debugMessagesTextEdit->appendPlainText( tr( "[DEBUG] Added '%1' to 'client_ips_to_clients_map': '%2'" ).arg( clients->last()->name ).arg( clientPointerAddressString ) );
}
}
QString *lcLablib::ReadSettingsItem( const QString &argVariableName, const QString &argComplaint, bool argItemIsFile ) {
if ( !labSettings.contains( argVariableName ) ) {
QMessageBox::information( nullptr, tr( "'%1' not set" ).arg( argVariableName ), tr( "The '%1' variable was not set. %2" ).arg( argVariableName ).arg( argComplaint ) );
debugMessagesTextEdit->appendPlainText( tr( "[DEBUG] '%1' was not set. %2" ).arg( argVariableName ).arg( argComplaint ) );
return nullptr;
}
else {
QString *tempString = new QString{ labSettings.value( argVariableName ).toString() };
if ( argItemIsFile && !CheckPathAndComplain( tempString, argVariableName, argComplaint ) ) {
delete tempString;
tempString = nullptr;
}
// Empty strings count as not set, so delete them for correct error handling in the rest of Labcontrol
if ( tempString && tempString->isEmpty() ) {
delete tempString;
tempString = nullptr;
}
return tempString;
}
return nullptr;
}
void lcLablib::SetAnonymousReceiptsPlaceholder( const QString &argPlaceHolder ) {
anonymousReceiptsPlaceholder = argPlaceHolder;
debugMessagesTextEdit->appendPlainText( tr( "[DEBUG] anonymous_receipts_placeholder set to: '%1'" ).arg( anonymousReceiptsPlaceholder ) );
}
void lcLablib::SetChosenLaTeXHeader( const QString &argLatexHeader ) {
chosenLaTeXHeader = argLatexHeader;
debugMessagesTextEdit->appendPlainText( tr( "[DEBUG] chosen_latex_header set to: '%1'" ).arg( chosenLaTeXHeader ) );
}
void lcLablib::SetChosenZTreeDataTargetPath( const QString &argZTreeDataTargetPath ) {
chosenZTreeDataTargetPath = argZTreeDataTargetPath;
debugMessagesTextEdit->appendPlainText( tr( "[DEBUG] chosen_zTree_data_target_path set to: '%1'" ).arg( chosenZTreeDataTargetPath ) );
}
void lcLablib::SetChosenZTreePort( const int &argPort ) {
chosenZTreePort = argPort;
debugMessagesTextEdit->appendPlainText( tr( "[DEBUG] chosen_zTree_port set to: '%1'" ).arg( QString::number( chosenZTreePort ) ) );
}
void lcLablib::SetChosenZTreeVersion( const QString &argZTreeVersion ) {
chosenZTreeVersion = QString{ "zTree_" + argZTreeVersion };
debugMessagesTextEdit->appendPlainText( tr( "[DEBUG] chosen_zTree_version set to: '%1'" ).arg( chosenZTreeVersion ) );
}
void lcLablib::SetPrintReceiptsForLocalClients( const bool &argPrintReceiptsForLocalClients ) {
PrintReceiptsForLocalClients = argPrintReceiptsForLocalClients;
debugMessagesTextEdit->appendPlainText( tr( "[DEBUG] Set print_receipts_for_local_clients to : '%1'" ).arg( QString::number( PrintReceiptsForLocalClients ) ) );
}
void lcLablib::SetUserNameOnServer( const QString &argUserName ) {
userNameOnServer = argUserName;
debugMessagesTextEdit->appendPlainText( tr( "[DEBUG] user_name_on_server set to: '%1'" ).arg( userNameOnServer ) );
}
void lcLablib::ShowOrsee() {
// Start the process
QProcess showOrseeProcess;
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
showOrseeProcess.setProcessEnvironment( env );
showOrseeProcess.startDetached( *( *settingsItems )[ ( int )settingsItems_t::ORSEE_COMMAND ] );
// Output message via the debug messages tab
debugMessagesTextEdit->appendPlainText( tr( "[DEBUG] %1" ).arg( *( *settingsItems )[ ( int )settingsItems_t::ORSEE_COMMAND ] ) );
}
void lcLablib::ShowPreprints() {
// Start the process
QProcess showPreprintsProcess;
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
showPreprintsProcess.setProcessEnvironment( env );
QString program{ *( *settingsItems )[ ( int )settingsItems_t::FILE_MANAGER ] };
QStringList arguments{ QStringList{} << *( *settingsItems )[ ( int )settingsItems_t::LABCONTROL_INSTALLATION_DIRECTORY ] + "/preprints" };
showPreprintsProcess.startDetached( program, arguments );
// Output message via the debug messages tab
debugMessagesTextEdit->appendPlainText( tr( "[DEBUG] %1 %2" ).arg( program ).arg( arguments.join( " " ) ) );
}
void lcLablib::StartNewZTreeInstance() {
if ( !QDir( chosenZTreeDataTargetPath ).exists() ) {
QMessageBox messageBox{ QMessageBox::Critical, tr( "Data target path does not exist" ),
tr( "Your chosen data target path does not exist. Do you want it to be created automatically?" ), QMessageBox::Yes | QMessageBox::No };
messageBox.exec();
if ( messageBox.clickedButton() == messageBox.button( QMessageBox::No ) ) {
QMessageBox messageBox{ QMessageBox::Critical, tr( "Data target directory will not be created" ),
tr( "Your chosen data target directory does not exist and will not be created. Please chose another one." ), QMessageBox::Ok };
messageBox.exec();
return;
}
else {
if ( !QDir().mkpath( chosenZTreeDataTargetPath ) ) {
QMessageBox messageBox{ QMessageBox::Critical, tr( "Data target directory could not be created" ),
tr( "Your chosen data target directory does not exist and could not be created. Please chose another one." ), QMessageBox::Ok };
messageBox.exec();
return;
}
}
}
try {
sessionsModel->push_back( new lcSession{ debugMessagesTextEdit, chosenZTreeDataTargetPath, chosenZTreePort, chosenZTreeVersion,
PrintReceiptsForLocalClients, anonymousReceiptsPlaceholder, chosenLaTeXHeader, settingsItems } );
occupiedPorts->append( sessionsModel->back()->zTreePort );
}
catch ( lcSession::lcDataTargetPathCreationFailed ) {
QMessageBox::information( nullptr, tr( "Chosen data target path could not be created" ),
tr( "The path specified by your chosen data target path '%1' could not be created. Please check if it is a valid location and you have all needed permissions." )
.arg( chosenZTreeDataTargetPath ) );
}
}
void lcLablib::SetLocalZLeafDefaultName( const QString &argName ) {
delete ( *settingsItems )[ ( int )settingsItems_t::LOCAL_ZLEAF_NAME ];
( *settingsItems )[ ( int )settingsItems_t::LOCAL_ZLEAF_NAME ] = new QString{ argName };
labSettings.setValue( "local_zLeaf_name", argName );
}

@ -0,0 +1,210 @@
/*
* Copyright 2014-2016 Markus Prasser
*
* This file is part of Labcontrol.
*
* Labcontrol is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Labcontrol is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Labcontrol. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef LABLIB_H
#define LABLIB_H
#include <list>
#include <QDir>
#include <QItemSelectionModel>
#include <QList>
#include <QMainWindow>
#include <QMap>
#include <QModelIndexList>
#include <QPlainTextEdit>
#include <QProcess>
#include <QSettings>
#include <QString>
#include <QTableView>
#include <QVector>
#include <QXmlStreamReader>
#include "client.h"
#include "clienthelpnotificationserver.h"
#include "global.h"
#include "netstatagent.h"
#include "session.h"
#include "sessionsmodel.h"
//! This class represents the entire lab and running sessions.
/*!
This class contains elements and functions needed to represent the lab and running sessions.
*/
class lcLablib : public QObject
{
Q_OBJECT
public:
/** Lablib's constructor
*
* @param argDebugMessagesTextEdit A pointer to the debug messages text edit for verbose output
* @param argParent This 'lcLablib' instance's parent QObject
*/
lcLablib( QPlainTextEdit *argDebugMessagesTextEdit, QObject *argParent = nullptr );
/** Lablib's destructor
*/
~lcLablib();
/*! Returns the users who have administrative rights
*
* @return The users with administrative rights
*/
QStringList *GetAdminUsers() const { return adminUsers; }
/*! Returns the placeholder for anonymous receipts
*
* @return The placeholder for anonymous receipts
*/
QString GetAnonymousReceiptsPlaceholder() const { return anonymousReceiptsPlaceholder; }
/*! Returns the currently set port number of zTree
*
* @return The currently set port number for zTree
*/
int GetChosenZTreePort() const { return chosenZTreePort; }
/** Returns a pointer to the clients known to Lablib
*
* @return A QVector of pointers to the Client class instances
*/
QVector<lcClient*> *GetClients () const { return clients; }
/** Returns the default receipt index for the 'CBReceipts' combobox
*
* @return The default receipt index for the 'CBReceipts' combobox
*/
int GetDefaultReceiptIndex () const { return defaultReceiptIndex; }
/** Returns a pointer to the QString storing the default name for local zLeafs
*
* @return A pointer to the QString storing the default name for local zLeafs
*/
QString *GetLocalZLeafDefaultName() const { return ( *settingsItems )[ ( int )settingsItems_t::LOCAL_ZLEAF_NAME ]; }
/** Returns a pointer to a QVector<unsigned int> containing all by sessions occupied ports
*
* @return A pointer to a QVector<unsigned int> containing all occupied ports
*/
QVector<int> *GetOccupiedPorts () const { return occupiedPorts; }
/** Returns a pointer to the QAbstractTableModel storing the Session instances
*
* @return A pointer to the QAbstractTableModel storing the Session instances
*/
SessionsModel *GetSessionsModel () const { return sessionsModel; }
/** Returns true if receipts for local clients shall be printed
*
* @return True if receipts for local clients shall be printed
*/
bool GetPrintReceiptsForLocalClients() const { return PrintReceiptsForLocalClients; }
/** Returns a pointer to a specified QString stored in 'settings_items' storing all command QString pointers
*
* @return A pointer to to a specified QString stored in 'settings_items' storing all command QString pointers
*/
QString *GetSettingsItem( settingsItems_t argItem ) const { return ( *settingsItems )[ ( int )argItem ]; }
/** Returns a pointer to the 'settingsItems' storing all command QString pointers
*
* @return A pointer to the 'settingsItems' storing all command QString pointers
*/
QVector<QString*> *GetSettingsItems() const { return settingsItems; }
/** Returns a pointer to server's user's name
*
* @return A pointer to the server's user's name
*/
QString GetUserNameOnServer () const { return userNameOnServer; }
/** Returns a QStringList containing all available LaTeX headers of this system
*
* @return A pointer to a QStringList containing all available LaTeX headers
*/
QStringList *GetInstalledLaTeXHeaders () const {return installedLaTeXHeaders; }
/** Returns a QStringList containing all available zTree versions of this system
*
* @return A pointer to a QStringList containing all available zTree versions
*/
QStringList *GetInstalledZTreeVersions () const { return InstalledZTreeVersions; }
/** Returns a pointer to a QStringList containing all available webcams
*
* @return A pointer to a QStringList containing all available webcams
*/
QStringList *GetWebcams () const { return webcams; }
void SetAnonymousReceiptsPlaceholder( const QString &argPlaceHolder );
void SetChosenLaTeXHeader( const QString &argLatexHeader );
void SetChosenZTreeDataTargetPath( const QString &argZTreeDataTargetPath );
void SetChosenZTreePort( const int &argPort );
void SetChosenZTreeVersion( const QString &argZTreeVersion );
//! Sets the default name of local zLeaf instances
/**
* @param argName The default name local zLeaf instances shall have
*/
void SetLocalZLeafDefaultName( const QString &argName );
void SetPrintReceiptsForLocalClients( const bool &argPrintReceiptsForLocalClients );
void SetUserNameOnServer( const QString &argUserName );
void ShowOrsee();
void ShowPreprints();
void StartNewZTreeInstance();
signals:
void ZLEAF_RUNNING( QString argClientIP );
private slots:
//! Gets the output from NetstatAgent
void GotNetstatQueryResult( QStringList *argActiveZLeafConnections );
private:
//! Checks if the specified path exists and complains if not so
/**
* @param argPath The path which shall be check for existance
* @param argVariableName The name of the QSettings variable referring the path
* @param argComplaint The complaint which shall be added to debugging output
* @return True, if path exists, false if not
*/
bool CheckPathAndComplain( const QString * const argPath, const QString &argVariableName, const QString &argComplaint );
//! Detects installed zTree version and LaTeX headers
void DetectInstalledZTreeVersionsAndLaTeXHeaders();
/** Reads all settings from the QSettings 'labSettings' object.
*/
void ReadSettings();
//! Loads a single settings item from the globally accessible QSettings instance
/**
* @param arg_variable_name The name of the variable to load
* @param arg_complaint The complaint to display if the variable could not be loaded
* @param arg_is_file If the existance of the file referenced by the setting's value shall be checked
* @return The settings data or a nullptr on failure
*/
QString *ReadSettingsItem( const QString &argVariableName, const QString &argComplaint, bool argItemIsFile = false );
QStringList *adminUsers = nullptr; //! Stores all users with administrative rights
QString anonymousReceiptsPlaceholder; //! The placeholder which shall substitute the client names on anonymous receipts (if != "")
QString chosenLaTeXHeader;
QString chosenZTreeDataTargetPath;
int chosenZTreePort = 7000; //! Stores the currently chosen port for new zTree instances
QString chosenZTreeVersion;
lcClientHelpNotificationServer *clientHelpNotificationServer = nullptr; //! A server to retrieve help requests from the clients
unsigned short int clientHelpNotificationServerPort = 0; //! The port the help requests shall be received on
QMap< QString, lcClient* > * clientIPsToClientsMap = nullptr; //! A map container storing ip-client pairs
QVector<lcClient*> *clients = nullptr; //! A QVector storing pointers to all Client instances
QPlainTextEdit *debugMessagesTextEdit = nullptr; //! This stores a pointer to the text edit in the debug tab to be able to write to it
int defaultReceiptIndex = 0; //! Stores the index of the LaTeX header to be displayed by default
QStringList *installedLaTeXHeaders = nullptr;
QStringList *InstalledZTreeVersions = nullptr;
QSettings labSettings;
lcNetstatAgent *netstatAgent = nullptr; //! Tries to detect active zLeaf connections from the clients
QThread netstatThread;
QTimer *netstatTimer = nullptr; //! A timer for regular execution of the NetstatAgent instance's request mechanism
QVector< int > *occupiedPorts = nullptr;
bool PrintReceiptsForLocalClients = true;
SessionsModel *sessionsModel = nullptr; //! A derivation from QAbstractTableModel used to store the single Session instances
QVector<QString*> *settingsItems = nullptr; //! A QVector storing all simple settings
QString userNameOnServer; //! The user name on the server as extracted from the environment variables
QStringList *webcams = nullptr; //! A QStringList containing all available stationary webcams in the laboratory
};
#endif // LABLIB_H

@ -0,0 +1,54 @@
/*
* Copyright 2014-2016 Markus Prasser
*
* This file is part of Labcontrol.
*
* Labcontrol is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Labcontrol is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Labcontrol. If not, see <http://www.gnu.org/licenses/>.
*/
#include "netstatagent.h"
lcNetstatAgent::lcNetstatAgent( QString *argNetstatCommand, QObject *argParent ) :
QObject{ argParent },
extractionRegexp{ "\\d+\\.\\d+\\.\\d+\\.\\d+" },
netstatArguments{ QStringList{} << "-anp" << "--tcp" },
netstatCommand{ *argNetstatCommand },
netstatQueryProcess{ this },
searchRegexp{ "\\W(ESTABLISHED|VERBUNDEN)( +)(\\d+)(/ztree.exe)\\W", QRegularExpression::CaseInsensitiveOption }
{
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
netstatQueryProcess.setProcessEnvironment( env );
}
void lcNetstatAgent::QueryClientConnections() {
netstatQueryProcess.start( netstatCommand, netstatArguments );
if ( !netstatQueryProcess.waitForFinished( 400 ) ) {
emit QueryFinished( nullptr );
} else {
// Get all 'netstat_query_process' standard output and store it temporarily in 'temp_strings'
QByteArray netstatQueryProcessOutputByteArray = netstatQueryProcess.readAllStandardOutput();
QString netstatQueryProcessOutputString( netstatQueryProcessOutputByteArray );
QStringList tempStrings = netstatQueryProcessOutputString.split( '\n', QString::SkipEmptyParts );
QStringList *netstatQueryProcessOutput = new QStringList;
for ( auto s: tempStrings ) {
if ( s.contains( searchRegexp ) ) {
QRegularExpressionMatch match = extractionRegexp.match( s, s.indexOf( ':', 0, Qt::CaseInsensitive ) );
netstatQueryProcessOutput->append( match.captured() );
}
}
emit QueryFinished(netstatQueryProcessOutput);
}
}

@ -0,0 +1,53 @@
/*
* Copyright 2014-2016 Markus Prasser
*
* This file is part of Labcontrol.
*
* Labcontrol is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Labcontrol is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Labcontrol. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef NETSTATAGENT_H
#define NETSTATAGENT_H
#include <QObject>
#include <QProcess>
#include <QRegularExpression>
#include <QStringList>
//! The NetstatAgent class is used to do repetitive runs of the 'netstat' program to check for active zLeaf connections.
/*!
This class is just used for repetive executions of netstat.
*/
class lcNetstatAgent : public QObject
{
Q_OBJECT
public:
explicit lcNetstatAgent( QString *argNetstatCommand, QObject *argParent = nullptr );
signals:
//! This signal is emitted if the query of the currently active zLeaf connections finished
void QueryFinished(QStringList *argActiveZLeafConnections);
public slots:
void QueryClientConnections();
private:
const QRegularExpression extractionRegexp;
const QStringList netstatArguments;
const QString netstatCommand;
QProcess netstatQueryProcess;
const QRegularExpression searchRegexp;
};
#endif // NETSTATAGENT_H

@ -0,0 +1,296 @@
/*
* Copyright 2014-2016 Markus Prasser
*
* This file is part of Labcontrol.
*
* Labcontrol is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Labcontrol is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Labcontrol. If not, see <http://www.gnu.org/licenses/>.
*/
#include "receipts_handler.h"
lcReceiptsHandler::lcReceiptsHandler( QPlainTextEdit *argDebugMessagesTextEdit, const QString &argZTreeDataTargetPath,
const bool &argPrintReceiptsForLocalClients, const QString &argAnonymousReceiptsPlaceholder,
const QString &argLatexHeaderName, const QVector< QString* > * const argSettingsItems, QObject *argParent ) :
QObject{ argParent },
anonymousReceiptsPlaceholder{ new QString{ argAnonymousReceiptsPlaceholder } },
settingsItems{ argSettingsItems },
debugMessagesTextEdit{ argDebugMessagesTextEdit },
latexHeaderName{ new QString{ argLatexHeaderName } },
printReceiptsForLocalClients{ new bool { argPrintReceiptsForLocalClients } },
timer{ new QTimer{ this } },
zTreeDataTargetPath{ new QString{ argZTreeDataTargetPath } }
{
// Guess the name of the payment file
QDateTime currentDate;
currentDate = QDateTime::currentDateTime();
dateString = new QString{ currentDate.toString( "yyMMdd_hhmm" ) };
expectedPaymentFileName = QString{ *dateString + ".pay" };
expectedPaymentFilePath = QString{ *zTreeDataTargetPath + "/" + *dateString + ".pay" };
debugMessagesTextEdit->appendPlainText( "[DEBUG] Expected payment file name is: " + expectedPaymentFilePath );
// Create a new file object representing the payment file
paymentFile = new QFile( expectedPaymentFilePath );
// Create a QTimer regularly checking if the payment file was created and print it if so
connect( timer, &QTimer::timeout, this, &lcReceiptsHandler::PrintReceipts );
timer->start( 2000 );
}
lcReceiptsHandler::lcReceiptsHandler( QPlainTextEdit *argDebugMessagesTextEdit, const QString &argZTreeDataTargetPath,
const bool &argPrintReceiptsForLocalClients, const QString &argAnonymousReceiptsPlaceholder,
const QString &argLatexHeaderName, const QVector< QString* > * const argSettingsItems, const QString * const argDateString, QObject *argParent ) :
QObject{ argParent },
anonymousReceiptsPlaceholder{ new QString{ argAnonymousReceiptsPlaceholder } },
settingsItems{ argSettingsItems },
dateString{ new QString{ *argDateString } },
debugMessagesTextEdit{ argDebugMessagesTextEdit },
latexHeaderName{ new QString{ argLatexHeaderName } },
printReceiptsForLocalClients{ new bool { argPrintReceiptsForLocalClients } },
zTreeDataTargetPath{ new QString{ argZTreeDataTargetPath } }
{
expectedPaymentFileName = QString{ *dateString + ".pay" };
expectedPaymentFilePath = QString{ *zTreeDataTargetPath + "/" + *argDateString + ".pay" };
debugMessagesTextEdit->appendPlainText( "[DEBUG] Expected payment file name is: " + expectedPaymentFilePath );
// Create a new file object representing the payment file
paymentFile = new QFile( expectedPaymentFilePath );
PrintReceipts();
}
lcReceiptsHandler::~lcReceiptsHandler() {
delete anonymousReceiptsPlaceholder;
delete dateString;
delete latexHeaderName;
delete paymentFile;
delete printReceiptsForLocalClients;
delete receiptsPrinter;
delete zTreeDataTargetPath;
}
void lcReceiptsHandler::PrintReceipts() {
// If the payment file exists, print it
if ( paymentFile->exists() ) {
debugMessagesTextEdit->appendPlainText( "[DEBUG] The payment file has been created and will be printed" );
if ( timer ) {
timer->stop();
}
CreateReceiptsFromPaymentFile();
}
}
void lcReceiptsHandler::CreateReceiptsFromPaymentFile() {
// Get the data needed for receipts creation from the payment file
QVector<QString> *rawParticipantsData = nullptr;
rawParticipantsData = GetParticipantsDataFromPaymentFile();
for ( int i = 0; i < rawParticipantsData->size(); i++ ) {
debugMessagesTextEdit->appendPlainText( "[DEBUG] Payment file line " + QString::number( i ) + ":\t" + rawParticipantsData->at( i ) );
}
// Extract the data of the participant's whose receipts shall be printed
/* The tab separated fields in the payment file are:
* SUBJECT COMPUTER INTERESTED NAME PROFIT SIGNATURE
*/
QVector<paymentEntry_t*> *participants = new QVector<paymentEntry_t*>;
double overall_payoff = 0.0;
for ( QVector<QString>::iterator it = rawParticipantsData->begin(); it != rawParticipantsData->end() - 1; ++it ) {
// Split the lines containing the participants' data into their inidivual parts
QStringList temp_participant_data = it->split('\t', QString::KeepEmptyParts);
debugMessagesTextEdit->appendPlainText( temp_participant_data.join( " - " ) );
if ( !( *printReceiptsForLocalClients ) && temp_participant_data.at( 3 ).contains( "local" ) ) {
debugMessagesTextEdit->appendPlainText( "Receipt for local client '" + temp_participant_data.at( 1 ) + "' will not be printed." );
}
else {
// Create a new struct instance for participant data and fill it
paymentEntry_t *participant = new paymentEntry_t;
participant->computer = temp_participant_data.at(1);
participant->name = temp_participant_data.at(3);
participant->payoff = temp_participant_data.at(4).toDouble();
overall_payoff += participant->payoff;
participants->append(participant);
}
}
delete rawParticipantsData;
rawParticipantsData = nullptr;
// Make receipts overview anonymous if requested (at this stage just names are removed, so that the overview still containts the client names
if ( !anonymousReceiptsPlaceholder->isEmpty() ) {
MakeReceiptsAnonymous( participants, false );
}
// Load the LaTeX header
QString *latexText = LoadLatexHeader();
if ( latexText == nullptr ) {
for ( auto s : *participants ) {
delete s;
}
delete participants;
participants = nullptr;
return;
}
// Write the comprehension table
latexText->append( "\n\\COMPREHENSION{\n" );
unsigned short int zeile = 0;
for ( auto s : *participants ) {
latexText->append( expectedPaymentFileName + " & " + s->computer + " & " + s->name + " & " + QString::number( s->payoff, 'f', 2 ) + " \\EUR\\\\\n" );
if ( zeile % 2 == 0 ) {
latexText->append( "\\rowcolor[gray]{0.9}\n" );
}
++zeile;
}
// MISSING: Appending show up entries to the overview
// Make also the clients on the receipts anonymous. This is done as second step, so that the beforehand created overview still contains the clients
if ( !anonymousReceiptsPlaceholder->isEmpty() ) {
MakeReceiptsAnonymous( participants, true );
}
// Add the LaTeX middle sequence
latexText->append( "}{" + QString::number( overall_payoff, 'f', 2 ) + "}\n\n%%Einzelquittungen\n" );
// Write the single receipts
for ( auto s : *participants ) {
if ( s->payoff >= 0 ) {
latexText->append( "\\GAINRECEIPT{" + expectedPaymentFileName + "}{" + s->computer + "}{" + s->name + "}{" + QString::number( s->payoff, 'f', 2 ) + "}\n" );
}
else {
latexText->append( "\\LOSSRECEIPT{" + expectedPaymentFileName + "}{" + s->computer + "}{" + s->name + "}{" + QString::number( s->payoff, 'f', 2 ) + "}\n" );
}
delete s;
}
delete participants;
participants = nullptr;
// Append LaTeX ending
latexText->append( "\\end{document}" );
debugMessagesTextEdit->appendPlainText( *latexText );
// Create the tex file
QFile *texFile = new QFile( *zTreeDataTargetPath + "/" + *dateString + ".tex" );
debugMessagesTextEdit->appendPlainText( "[DEBUG] Tex file '" + texFile->fileName() + "' will be created for receipts printing." );
// Clean up any already existing files
if ( texFile->exists() ) {
if ( !texFile->remove() ) {
QMessageBox messageBox( QMessageBox::Critical, "Tex file removing failed", "There already exists a tex file at '" + texFile->fileName()
+ "' which cannot be removed. The creation of the receipts printout may fail.", QMessageBox::Ok );
messageBox.exec();
}
}
// Create a new file
if ( !texFile->open( QIODevice::Text | QIODevice::WriteOnly ) ) {
QMessageBox messageBox( QMessageBox::Critical, "Tex file creation failed", "The creation of the tex file for receipts printing at '" + texFile->fileName()
+ "' failed. Receipts printing will not work.", QMessageBox::Ok );
messageBox.exec();
return;
}
// Open a QTextStream to write to the file
QTextStream out( texFile );
out << *latexText;
delete latexText;
latexText = nullptr;
receiptsPrinter = new lcReceiptsPrinter{ dateString, zTreeDataTargetPath, settingsItems };
receiptsPrinter->start();
connect( receiptsPrinter, &lcReceiptsPrinter::PrintingFinished, this, &lcReceiptsHandler::DeleteReceiptsPrinterInstance );
connect( receiptsPrinter, &lcReceiptsPrinter::ErrorOccurred, this, &lcReceiptsHandler::DisplayMessageBox );
// Clean up
texFile->close();
delete texFile;
}
void lcReceiptsHandler::DeleteReceiptsPrinterInstance() {
receiptsPrinter->quit();
receiptsPrinter->wait();
delete receiptsPrinter;
receiptsPrinter = nullptr;
debugMessagesTextEdit->appendPlainText( "[DEBUG] Deleted ReceiptsPrinter instance." );
emit PrintingFinished();
}
void lcReceiptsHandler::DisplayMessageBox( QString *argErrorMessage, QString *argHeading ) {
QMessageBox messageBox( QMessageBox::Warning, *argHeading, *argErrorMessage, QMessageBox::Ok );
delete argHeading;
delete argErrorMessage;
messageBox.exec();
}
QVector<QString> *lcReceiptsHandler::GetParticipantsDataFromPaymentFile() {
// Create the vector to store the single lines of the file
QVector<QString> *participantsData = new QVector<QString>;
// Open the payment file for reading and create a QTextStream
paymentFile->open( QIODevice::ReadOnly | QIODevice::Text );
QTextStream in( paymentFile );
in.setCodec( "ISO 8859-1" );
// Read the file line by line and store them in the vector
while ( true ) {
QString line = in.readLine();
if ( line.isNull() ) {
break;
}
participantsData->append( line );
}
// Remove the first line, since it is not needed
participantsData->erase( participantsData->begin() );
// Close the file afterwards
paymentFile->close();
return participantsData;
}
QString *lcReceiptsHandler::LoadLatexHeader() {
// Prepare all facilities to read the latex header file
QFile latexHeaderFile( *( *settingsItems )[ ( int )settingsItems_t::LABCONTROL_INSTALLATION_DIRECTORY ] + "/" + *latexHeaderName + "_header.tex" );
if ( !latexHeaderFile.open( QIODevice::ReadOnly | QIODevice::Text ) ) {
QMessageBox messageBox{ QMessageBox::Critical, tr( "LaTeX header could not be loaded" ), tr( "The LaTeX header at '%1/%2_header.tex' could not be loaded. Receipts printing will not work." )
.arg( *( *settingsItems )[ ( int )settingsItems_t::LABCONTROL_INSTALLATION_DIRECTORY ] ).arg( *latexHeaderName ), QMessageBox::Ok };
messageBox.exec();
return nullptr;
}
QTextStream in( &latexHeaderFile );
QString *header = nullptr;
header = new QString( in.readAll() );
latexHeaderFile.close();
return header;
}
void lcReceiptsHandler::MakeReceiptsAnonymous( QVector<paymentEntry_t*> *argDataVector, bool argAlsoAnonymizeClients ) {
if ( !argAlsoAnonymizeClients ) {
debugMessagesTextEdit->appendPlainText( "[DEBUG] Names are made anonymous" );
for ( QVector< paymentEntry_t* >::iterator it = argDataVector->begin(); it != argDataVector->end(); ++it ) {
( *it )->name = QString{ *anonymousReceiptsPlaceholder };
}
}
else {
debugMessagesTextEdit->appendPlainText( "[DEBUG] Clients and names are made anonymous" );
for ( QVector< paymentEntry_t* >::iterator it = argDataVector->begin(); it != argDataVector->end(); ++it ) {
( *it )->name = QString{ *anonymousReceiptsPlaceholder };
( *it )->computer = QString{ "\\hspace{1cm}" };
}
}
}

@ -0,0 +1,90 @@
/*
* Copyright 2014-2016 Markus Prasser
*
* This file is part of Labcontrol.
*
* Labcontrol is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Labcontrol is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Labcontrol. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef RECEIPTS_HANDLER_H
#define RECEIPTS_HANDLER_H
#include <QDateTime>
#include <QFile>
#include <QMessageBox>
#include <QObject>
#include <QPlainTextEdit>
#include <QTextStream>
#include <QTimer>
#include "receiptsprinter.h"
//! A struct representing one payoff entry.
/*!
This class represents a single payoff entry which will be used in the receipts creation process. Multiple instances of this will be used to represent the individual participants' outcomes.
*/
struct paymentEntry_t {QString computer; QString name; double payoff;};
//! A class to handle receipts printing.
/*!
This class is element of every session and is used to handle the receipts printing.
*/
class lcReceiptsHandler : public QObject
{
Q_OBJECT
public:
explicit lcReceiptsHandler( QPlainTextEdit *argDebugMessagesTextEdit, const QString &argZTreeDataTargetPath,
const bool &argPrintReceiptsForLocalClients, const QString &argAnonymousReceiptsPlaceholder,
const QString &argLatexHeaderName, const QVector<QString*> * const argSettingsItems, QObject *argParent = nullptr );
explicit lcReceiptsHandler( QPlainTextEdit *argDebugMessagesTextEdit, const QString &argZTreeDataTargetPath,
const bool &argPrintReceiptsForLocalClients, const QString &argAnonymousReceiptsPlaceholder,
const QString &argLatexHeaderName, const QVector<QString*> * const argSettingsItems,
const QString * const argDateString, QObject *argParent = nullptr );
~lcReceiptsHandler();
signals:
void PrintingFinished();
private slots:
/*! Deletes the ReceiptsPrinter instance after successful printing
*/
void DeleteReceiptsPrinterInstance();
/*! Displays QMessageBox widgets for errors given by the ReceiptsPrinter instance
*/
void DisplayMessageBox(QString *argErrorMessage, QString *argHeading);
/*! Prints the receipts
*/
void PrintReceipts();
private:
void CreateReceiptsFromPaymentFile();
QVector<QString> *GetParticipantsDataFromPaymentFile();
QString *LoadLatexHeader();
void MakeReceiptsAnonymous( QVector<paymentEntry_t*> *argDataVector, bool argAlsoAnonymizeClients );
const QString * const anonymousReceiptsPlaceholder; //! Placeholder which shall be inserted for participant names if anonymous printing is desired (QString != "")
const QVector<QString*> * const settingsItems; //! A QVector storing all needed command paths
QString *dateString = nullptr;
QPlainTextEdit * const debugMessagesTextEdit = nullptr; //! A pointer to the programs debug_messages_text_edit to be able to emit debugging messages
QString expectedPaymentFileName; //! The name of the expected payment file
QString expectedPaymentFilePath; //! The path of the expected payment file
const QString * const latexHeaderName; //! The name of the chosen LaTeX header template
QFile *paymentFile = nullptr; //! A pointer to the '*.pay' file being watched for existance and starting the printing process
const bool * const printReceiptsForLocalClients; //! Stores if receipts shall be printed for local clients
lcReceiptsPrinter *receiptsPrinter = nullptr; //! Creates new thread for receipts printing
QTimer *timer = nullptr; //! Used for regular checking if the payment file was created
const QString * const zTreeDataTargetPath; //! A reference to the data target path stored in the session class instance
};
#endif // RECEIPTS_HANDLER_H

@ -0,0 +1,28 @@
/*
* Copyright 2014-2016 Markus Prasser
*
* This file is part of Labcontrol.
*
* Labcontrol is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Labcontrol is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Labcontrol. If not, see <http://www.gnu.org/licenses/>.
*/
#include "receiptsprinter.h"
lcReceiptsPrinter::lcReceiptsPrinter( const QString * const argDateString, const QString * const argWorkpath, const QVector< QString* > * const argSettingsItems, QObject *argParent ) :
QThread{ argParent },
dateString{ argDateString },
settingsItems{ argSettingsItems },
workpath{ argWorkpath }
{
}

@ -0,0 +1,151 @@
/*
* Copyright 2014-2016 Markus Prasser
*
* This file is part of Labcontrol.
*
* Labcontrol is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Labcontrol is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Labcontrol. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef RECEIPTSPRINTER_H
#define RECEIPTSPRINTER_H
#include <QMessageBox>
#include <QProcess>
#include <QThread>
#include "global.h"
//! A class for receipts creation.
/*!
This class is used to do the actual printing of the receipts in an own thread.
*/
class lcReceiptsPrinter : public QThread {
Q_OBJECT
void run() Q_DECL_OVERRIDE {
// Compile the TeX file to dvi
QStringList arguments;
arguments << "-interaction" << "batchmode" << QString{ *dateString + ".tex" };
QProcess *process = nullptr;
process = new QProcess{};
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
process->setProcessEnvironment( env );
process->setWorkingDirectory( *workpath );
process->start( *( *settingsItems )[ ( int )settingsItems_t::LATEX_COMMAND ], arguments );
if( !process->waitForFinished( processTimeOut ) ) {
QMessageBox message_box{ QMessageBox::Warning, "dvi creation failed", "The creation of the receipts dvi timed out after 30 seconds. Automatic receipts creation will not work.", QMessageBox::Ok };
message_box.exec();
delete process;
process = nullptr;
return;
}
delete process;
process = nullptr;
// Convert the dvi file to postscript
arguments = QStringList{};
arguments << "-q*" << "-o" << QString{ *dateString + ".ps" } << QString{ *dateString + ".dvi" };
process = new QProcess{};
process->setProcessEnvironment( env );
process->setWorkingDirectory( *workpath );
process->start( *( *settingsItems )[ ( int )settingsItems_t::DVIPS_COMMAND ], arguments );
if( !process->waitForFinished( processTimeOut ) ) {
emit ErrorOccurred(new QString{ "The conversion of the receipts dvi to postscript timed out after 30 seconds. Automatic receipts creation will not work." }, new QString{ "dvi to postscript conversion failed" } );
delete process;
process = nullptr;
return;
}
delete process;
process = nullptr;
// Print the postscript file
if ( ( *settingsItems )[ ( int )settingsItems_t::LPR_COMMAND ] ) {
arguments = QStringList{};
arguments << QString{ *workpath + "/" + *dateString + ".ps" };
process = new QProcess{};
process->setProcessEnvironment( env );
process->setWorkingDirectory( *workpath );
process->start( *( *settingsItems )[ ( int )settingsItems_t::LPR_COMMAND ], arguments );
if( !process->waitForFinished( processTimeOut ) ) {
emit ErrorOccurred( new QString{ "The receipts postscript file was successfully created but could not be printed." }, new QString{ "Printing failed" } );
}
delete process;
process = nullptr;
}
// Convert the postscript file to pdf
if ( ( *settingsItems )[ ( int )settingsItems_t::PS2PDF_COMMAND ] ) {
arguments = QStringList{};
arguments << QString{ *workpath + "/" + *dateString + ".ps" } << QString{ *workpath + "/" + *dateString + ".pdf" };
process = new QProcess{};
process->setProcessEnvironment( env );
process->setWorkingDirectory( *workpath );
process->start( *( *settingsItems )[ ( int )settingsItems_t::PS2PDF_COMMAND ], arguments );
if( !process->waitForFinished( processTimeOut ) ) {
emit ErrorOccurred( new QString{ "The receipts were successfully printed but the creation of the PDF file failed." }, new QString{ "PDF creation failed" } );
}
delete process;
process = nullptr;
// Show the postscript file if the conversion succeeded
if ( ( *settingsItems )[ ( int )settingsItems_t::POSTSCRIPT_VIEWER ] ) {
arguments = QStringList{};
arguments << QString{ *workpath + "/" + *dateString + ".ps" };
process = new QProcess{};
process->setProcessEnvironment( env );
process->setWorkingDirectory( *workpath );
process->startDetached( *( *settingsItems )[ ( int )settingsItems_t::POSTSCRIPT_VIEWER ], arguments );
delete process;
process = nullptr;
}
}
// Clean up the zTree working path
if ( ( *settingsItems )[ ( int )settingsItems_t::RM_COMMAND ] ) {
arguments = QStringList{};
arguments << QString{ *workpath + "/" + *dateString + ".aux" } << QString{ *workpath + "/" + *dateString + ".dvi" }
<< QString{ *workpath + "/" + *dateString + ".log" } << QString{ *workpath + "/" + *dateString + ".tex" };
process = new QProcess{};
process->setProcessEnvironment( env );
process->setWorkingDirectory( *workpath );
process->start( *( *settingsItems )[ ( int )settingsItems_t::RM_COMMAND ], arguments);
if( !process->waitForFinished( processTimeOut ) ) {
emit ErrorOccurred(new QString("The cleanup of the temporary files for receipts creation timed out. Some spare files may be left in your zTree working directory."), new QString("Cleanup failed"));
}
delete process;
process = nullptr;
}
emit PrintingFinished();
}
public:
explicit lcReceiptsPrinter( const QString * const argDateString, const QString * const argWorkpath, const QVector< QString* > * const argSettingsItems, QObject *argParent = nullptr );
signals:
void ErrorOccurred(QString *error_message, QString *heading);
void PrintingFinished();
private:
const QString * const dateString; //! The date string contained in the file paths
const int processTimeOut = 15000; //! The maximum time which will be granted to a started process
const QVector<QString*> * const settingsItems; //! A QVector storing all needed command paths
const QString * const workpath; //! The path were zTree was ordered to store all its data
};
#endif // RECEIPTSPRINTER_H

@ -0,0 +1,100 @@
/*
* Copyright 2014-2016 Markus Prasser
*
* This file is part of Labcontrol.
*
* Labcontrol is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Labcontrol is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Labcontrol. If not, see <http://www.gnu.org/licenses/>.
*/
#include "session.h"
lcSession::lcSession( QPlainTextEdit * const argDebugMessagesTextEdit, const QString &argZTreeDataTargetPath, const int argZTreePort,
const QString &argZTreeVersionPath, bool argPrintReceiptsForLocalClients, const QString &argAnonymousReceiptsPlaceholder,
const QString &argLatexHeaderName, const QVector<QString*> * const argSettingsItems ):
zTreePort{ argZTreePort },
anonymousReceiptsPlaceholder{ argAnonymousReceiptsPlaceholder },
debugMessagesTextEdit{ argDebugMessagesTextEdit },
latexHeaderName{ argLatexHeaderName },
printReceiptsForLocalClients{ argPrintReceiptsForLocalClients },
settingsItems{ argSettingsItems },
zTreeDataTargetPath{ argZTreeDataTargetPath },
zTreeVersionPath{ argZTreeVersionPath }
{
// This part ensures, that both class instances are created in the same minute, so that the payment file name can be guessed correctly
QDateTime current_time;
current_time = QDateTime::currentDateTime();
// If in the last three seconds of a minute, wait for the next one to start
if ( QTime::currentTime().second() > 56 ) {
QTimer::singleShot( 5000, this, SLOT( InitializeClasses() ) );
} else {
InitializeClasses();
}
if ( ( *settingsItems )[ ( int )settingsItems_t::WMCTRL_COMMAND ] ) {
QTimer::singleShot( 5000, this, SLOT( RenameWindow() ) );
}
}
lcSession::~lcSession() {
delete receiptsHandler;
delete zTreeInstance;
}
QVariant lcSession::GetDataItem( int argIndex ) {
switch ( argIndex ) {
case 0:
return QVariant{ zTreeVersionPath.split( '_', QString::KeepEmptyParts, Qt::CaseInsensitive )[ 1 ] };
case 1:
return QVariant{ zTreePort };
default:
return QVariant{};
}
}
void lcSession::InitializeClasses() {
// Create the new data directory
QDir dir{ zTreeDataTargetPath };
QString date_string( QDateTime::currentDateTime().toString( "yyMMdd_hhmm" ) );
if ( !dir.mkdir( zTreeDataTargetPath + "/" + date_string + "-" + QString::number( zTreePort ) ) ) {
throw lcDataTargetPathCreationFailed{};
}
zTreeDataTargetPath.append( "/" + date_string + "-" + QString::number( zTreePort ) );
debugMessagesTextEdit->appendPlainText( "[DEBUG] New session's chosen_zTree_data_target_path: " + zTreeDataTargetPath );
zTreeInstance = new lcZTree{ debugMessagesTextEdit, zTreeDataTargetPath, zTreePort, zTreeVersionPath, settingsItems };
// Only create a 'Receipts_Handler' instance, if all neccessary variables were set
if ( latexHeaderName != "None found" && ( *settingsItems )[ ( int )settingsItems_t::DVIPS_COMMAND ] && ( *settingsItems )[ ( int )settingsItems_t::LATEX_COMMAND ] ) {
receiptsHandler = new lcReceiptsHandler{ debugMessagesTextEdit, zTreeDataTargetPath, printReceiptsForLocalClients, anonymousReceiptsPlaceholder, latexHeaderName, settingsItems };
} else {
debugMessagesTextEdit->appendPlainText( tr( "[DEBUG] No ReceiptsHandler instance was created." ) );
}
}
void lcSession::RenameWindow() {
// Example: wmctrl -r <window name> -T <new name>
QStringList arguments;
arguments << "-r" << "zTree - Untitled Treatment 1" << "-T" << QString{ "zTree on port " + QString::number( zTreePort ) };
// Start the process
QProcess renameZTreeWindowProcess;
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
renameZTreeWindowProcess.setProcessEnvironment( env );
renameZTreeWindowProcess.startDetached( *( *settingsItems )[ ( int )settingsItems_t::WMCTRL_COMMAND ], arguments );
debugMessagesTextEdit->appendPlainText( "[DEBUG] Renamed window" );
// emit session_started();
}

@ -0,0 +1,76 @@
/*
* Copyright 2014-2016 Markus Prasser
*
* This file is part of Labcontrol.
*
* Labcontrol is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Labcontrol is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Labcontrol. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SESSION_H
#define SESSION_H
#include <QFileSystemWatcher>
#include <QTimer>
#include "receipts_handler.h"
#include "ztree.h"
//! A class containing an entire session.
/*!
This class represents an entire session with its zTree instance and the corresponding lcReceiptsHandler instance.
*/
class lcSession : public QObject
{
Q_OBJECT
public:
const int zTreePort = 7000; //! The port this session's zTree instance is running on
lcSession( QPlainTextEdit * const argDebugMessagesTextEdit, const QString &argZTreeDataTargetPath, const int argZTreePort,
const QString &argZTreeVersionPath, bool argPrintReceiptsForLocalClients, const QString &argAnonymousReceiptsPlaceholder,
const QString &argLatexHeaderName, const QVector<QString*> * const argSettingsItems );
~lcSession();
/*! Returns the data item with the given index
*
* @param argIndex The index of the desired item
*/
QVariant GetDataItem( int argIndex );
//! This gets thrown as an exception if the chosen data target path could not be created.
class lcDataTargetPathCreationFailed {};
signals:
// void session_started();
private slots:
/*! Starts the session by creating instances of the relevant classes
*/
void InitializeClasses();
/*! Changes zTree's window title to contain its port number to make zTree windows distinguishable
*/
void RenameWindow();
private:
const QString anonymousReceiptsPlaceholder; //! Placeholder which shall be inserted for participant names if anonymous printing is desired (QString != "")
QPlainTextEdit * const debugMessagesTextEdit = nullptr; //! A pointer to the programs debugMessagesTextEdit to be able to emit debugging messages
const QString latexHeaderName; //! The name of the chosen LaTeX header
const bool printReceiptsForLocalClients = true; //! True if receipts shall be printed for local clients
lcReceiptsHandler *receiptsHandler = nullptr; //! For automatic creation and printing of the receipts
const QVector<QString*> * const settingsItems; //! A QVector container storing all needed command paths
QString zTreeDataTargetPath; //! The path were the data of this zTree instance's session will be saved
lcZTree *zTreeInstance= nullptr; //! The session's zTree instance
const QString zTreeVersionPath; //! The path to the version of zTree used by this session's instance
};
#endif // SESSION_H

@ -0,0 +1,81 @@
/*
* Copyright 2014-2016 Markus Prasser
*
* This file is part of Labcontrol.
*
* Labcontrol is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Labcontrol is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Labcontrol. If not, see <http://www.gnu.org/licenses/>.
*/
#include "sessionsmodel.h"
SessionsModel::SessionsModel(QObject *parent) :
QAbstractTableModel{parent},
sessions_vector{ new QVector< lcSession* > }
{
}
SessionsModel::~SessionsModel() {
for (auto s: *sessions_vector)
delete s;
delete sessions_vector;
}
lcSession *SessionsModel::back() const {
return sessions_vector->back();
}
int SessionsModel::columnCount(const QModelIndex &parent) const {
Q_UNUSED(parent);
return 2;
}
QVariant SessionsModel::data(const QModelIndex &index, int role) const {
if (!index.isValid())
return QVariant{};
if (index.row() >= sessions_vector->size() || index.row() < 0)
return QVariant{};
if (role == Qt::DisplayRole)
return sessions_vector->at( index.row() )->GetDataItem( index.column() );
return QVariant{};
}
QVariant SessionsModel::headerData(int section, Qt::Orientation orientation, int role) const {
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
switch(section) {
case 0:
return tr("zTree Version");
case 1:
return tr("Port");
case 2:
return tr("LaTeX Header");
case 3:
return tr("Anonymous Receipts Placeholder");
default:
return QVariant{};
}
}
return QVariant{};
}
void SessionsModel::push_back( lcSession *argSession ) {
sessions_vector->push_back( argSession );
}
int SessionsModel::rowCount(const QModelIndex &parent) const {
Q_UNUSED(parent);
return sessions_vector->length();
}

@ -0,0 +1,50 @@
/*
* Copyright 2014-2016 Markus Prasser
*
* This file is part of Labcontrol.
*
* Labcontrol is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Labcontrol is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Labcontrol. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SESSIONSMODEL_H
#define SESSIONSMODEL_H
#include <QAbstractTableModel>
#include "session.h"
class SessionsModel : public QAbstractTableModel
{
Q_OBJECT
public:
explicit SessionsModel(QObject *parent = 0);
~SessionsModel();
SessionsModel(const SessionsModel&) = delete;
lcSession *back() const;
int columnCount(const QModelIndex &parent) const;
QVariant data(const QModelIndex &index, int role) const;
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
void push_back( lcSession *argSession );
int rowCount(const QModelIndex &parent) const;
signals:
public slots:
private:
QVector<lcSession*> *sessions_vector;
};
#endif // SESSIONSMODEL_H

@ -0,0 +1,38 @@
/*
* Copyright 2014-2016 Markus Prasser
*
* This file is part of Labcontrol.
*
* Labcontrol is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Labcontrol is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Labcontrol. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ztree.h"
lcZTree::lcZTree( QPlainTextEdit *argDebugMessagesTextEdit, const QString &argZTreeDataTargetPath, const int &argZTreePort, const QString &argZTreeVersionPath, const QVector<QString*> * const argSettingsItems ) {
QString program{ *( *argSettingsItems )[ ( int )settingsItems_t::LABCONTROL_INSTALLATION_DIRECTORY ] + "/scripts/start_zTree_labcontrol2.sh" };
QStringList arguments;
arguments << *( *argSettingsItems )[ ( int )settingsItems_t::ZTREE_INSTALLATION_DIRECTORY ] << argZTreeVersionPath << argZTreeDataTargetPath << QString::number( static_cast<int>( argZTreePort ) - 7000 );
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
zTreeInstance.setProcessEnvironment( env );
zTreeInstance.startDetached( program, arguments, QDir::currentPath(), &pid );
connect( &zTreeInstance, SIGNAL( finished( int ) ), SLOT( ZTreeInstanceClosed() ) );
// Output message via the debug messages tab
argDebugMessagesTextEdit->appendPlainText( "[DEBUG] " + program + " " + arguments.join( " " ) );
}
void lcZTree::ZTreeInstanceClosed() {
emit ZTreeClosed();
}

@ -0,0 +1,49 @@
/*
* Copyright 2014-2016 Markus Prasser
*
* This file is part of Labcontrol.
*
* Labcontrol is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Labcontrol is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Labcontrol. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ZTREE_H
#define ZTREE_H
#include <QDir>
#include <QPlainTextEdit>
#include <QProcess>
#include "global.h"
//! A class to contain running zTree instances.
/*!
This class is element of every session and is used to handle all zTree related data.
*/
class lcZTree: public QObject {
Q_OBJECT
public:
lcZTree( QPlainTextEdit *argDebugMessagesTextEdit, const QString &argZTreeDataTargetPath, const int &argZTreePort, const QString &argZTreeVersionPath, const QVector<QString*> * const argSettingsItems );
signals:
void ZTreeClosed();
private slots:
void ZTreeInstanceClosed();
private:
qint64 pid = 0;
QProcess zTreeInstance;
};
#endif // ZTREE_H

@ -0,0 +1,22 @@
#-------------------------------------------------
#
# Project created by QtCreator 2015-03-24T18:44:00
#
#-------------------------------------------------
QT += core gui network
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = HelpMessageSender
TEMPLATE = app
SOURCES += main.cpp\
helpmessagewindow.cpp
HEADERS += helpmessagewindow.h
FORMS += helpmessagewindow.ui
QMAKE_CXXFLAGS += -std=c++11

@ -0,0 +1,107 @@
#include "helpmessagewindow.h"
#include <iostream>
lcHelpMessageWindow::lcHelpMessageWindow( const QString &argServerIP, const unsigned short int &argServerPort, QWidget *argParent ) :
QMainWindow{ argParent },
helpMessageSocket { new QTcpSocket{ this } },
serverPort{ argServerPort },
serverAddress{ argServerIP },
ui{ new Ui::HelpMessageWindow }
{
ui->setupUi( this );
connect( ui->PBAskForHelp, &QPushButton::clicked, this, &lcHelpMessageWindow::RequestHelp );
connect( helpMessageSocket, &QTcpSocket::readyRead, this, &lcHelpMessageWindow::ReadHelpReply );
connect( helpMessageSocket, SIGNAL( error( QAbstractSocket::SocketError ) ),
this, SLOT( DisplayError( QAbstractSocket::SocketError ) ) );
QNetworkConfigurationManager manager;
if ( manager.capabilities() & QNetworkConfigurationManager::NetworkSessionRequired ) {
// Get saved network configuration
QSettings settings{ QSettings::UserScope, QLatin1String{ "QtProject" } };
settings.beginGroup( QLatin1String{ "QtNetwork" } );
const QString id = settings.value( QLatin1String{ "DefaultNetworkConfiguration" } ).toString();
settings.endGroup();
// If the saved network configuration is not currently discovered use the system default
QNetworkConfiguration config = manager.configurationFromIdentifier( id );
if ( ( config.state() & QNetworkConfiguration::Discovered ) != QNetworkConfiguration::Discovered ) {
config = manager.defaultConfiguration();
}
networkSession = new QNetworkSession{ config, this };
connect( networkSession, &QNetworkSession::opened, this, &lcHelpMessageWindow::OpenedSession );
networkSession->open();
}
}
lcHelpMessageWindow::~lcHelpMessageWindow() {
delete ui;
}
void lcHelpMessageWindow::DisplayError( QAbstractSocket::SocketError socketError ) {
QString errorMessage;
switch ( socketError ) {
case QAbstractSocket::RemoteHostClosedError:
return;
case QAbstractSocket::HostNotFoundError:
errorMessage = tr( "An error occurred: The server could not be found for error reporting:\n" );
break;
case QAbstractSocket::ConnectionRefusedError:
errorMessage = tr( "An error occurred: The connection was refused by the laboratory server:\n" );
break;
default:
errorMessage = tr( "The following error occured:\n" );
}
errorMessage.append( tr("%1").arg( helpMessageSocket->errorString() ) );
errorMessage.append( "\n\nPlease raise your hand to notify the experimenters." );
ui->LSendingSuccess->setText( errorMessage );
}
void lcHelpMessageWindow::OpenedSession() {
// Save the used configuration
QNetworkConfiguration config = networkSession->configuration();
QString id;
if ( config.type() == QNetworkConfiguration::UserChoice )
id = networkSession->sessionProperty( QLatin1String{ "UserChoiceConfiguration" } ).toString();
else
id = config.identifier();
QSettings settings{ QSettings::UserScope, QLatin1String{ "QtProject" } };
settings.beginGroup( QLatin1String{ "QtNetwork" } );
settings.setValue( QLatin1String{ "DefaultNetworkConfiguration" }, id );
settings.endGroup();
}
void lcHelpMessageWindow::ReadHelpReply() {
QDataStream in( helpMessageSocket );
in.setVersion( QDataStream::Qt_5_2 );
if ( blockSize == 0 ) {
if ( helpMessageSocket->bytesAvailable() < ( int )sizeof( quint16 ) )
return;
in >> blockSize;
}
if ( helpMessageSocket->bytesAvailable() < blockSize ) {
return;
}
QString serverAnswer;
in >> serverAnswer;
if ( serverAnswer == "Help demand retrieved." ) {
ui->LSendingSuccess->setText( tr( "Help message successfully sent.\nPlease wait for the experimenter to show up at your booth." ) );
ui->PBAskForHelp->setEnabled( false );
} else {
ui->LSendingSuccess->setText( tr( "An error occurred sending the help message. Please raise your arm.\n\n'%1'" ).arg( serverAnswer ) );
}
}
void lcHelpMessageWindow::RequestHelp() {
blockSize = 0;
helpMessageSocket->abort();
helpMessageSocket->connectToHost( serverAddress, serverPort );
}

@ -0,0 +1,37 @@
#ifndef HELPMESSAGEWINDOW_H
#define HELPMESSAGEWINDOW_H
#include "ui_helpmessagewindow.h"
#include <QMainWindow>
#include <QMessageBox>
#include <QtNetwork>
namespace Ui {
class HelpMessageWindow;
}
class lcHelpMessageWindow : public QMainWindow
{
Q_OBJECT
public:
explicit lcHelpMessageWindow( const QString &argServerIP, const unsigned short int &argServerPort, QWidget *argParent = nullptr );
~lcHelpMessageWindow();
private:
quint16 blockSize = 0;
QTcpSocket *helpMessageSocket = nullptr;
QNetworkSession *networkSession = nullptr;
const quint16 serverPort = 0;
const QHostAddress serverAddress;
Ui::HelpMessageWindow *ui;
private slots:
void RequestHelp();
void ReadHelpReply();
void DisplayError( QAbstractSocket::SocketError socketError );
void OpenedSession();
};
#endif // HELPMESSAGEWINDOW_H

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>HelpMessageWindow</class>
<widget class="QMainWindow" name="HelpMessageWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>512</width>
<height>192</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>512</width>
<height>192</height>
</size>
</property>
<property name="windowTitle">
<string>HelpMessageSender</string>
</property>
<widget class="QWidget" name="CWHelpMessageWindow">
<layout class="QVBoxLayout" name="VLHelpMessageWindow">
<item>
<widget class="QPushButton" name="PBAskForHelp">
<property name="text">
<string>Ask for help</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="LSendingSuccess">
<property name="text">
<string>Please click on the &quot;Ask for help&quot; button to send a help demand message to the experimenters.</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="PBQuit">
<property name="text">
<string>Quit</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections>
<connection>
<sender>PBQuit</sender>
<signal>clicked()</signal>
<receiver>HelpMessageWindow</receiver>
<slot>deleteLater()</slot>
<hints>
<hint type="sourcelabel">
<x>191</x>
<y>105</y>
</hint>
<hint type="destinationlabel">
<x>191</x>
<y>63</y>
</hint>
</hints>
</connection>
</connections>
</ui>

@ -0,0 +1,12 @@
#include "helpmessagewindow.h"
#include <QApplication>
int main( int argc, char *argv[] )
{
QApplication a( argc, argv );
QSettings labSettings{ "Economic Laboratory", "Labcontrol" };
lcHelpMessageWindow w{ labSettings.value( "server_ip", "127.0.0.1" ).toString(), labSettings.value( "client_help_server_port", "0" ).toUInt() };
w.show();
return a.exec();
}

@ -0,0 +1,29 @@
/*
* Copyright 2014-2016 Markus Prasser
*
* This file is part of Labcontrol.
*
* Labcontrol is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Labcontrol is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Labcontrol. If not, see <http://www.gnu.org/licenses/>.
*/
#include "mainwindow.h"
#include <QApplication>
int main( int argc, char *argv[] ) {
QApplication a( argc, argv );
lcMainWindow w;
w.show();
return a.exec();
}

@ -0,0 +1,765 @@
/*
* Copyright 2014-2016 Markus Prasser
*
* This file is part of Labcontrol.
*
* Labcontrol is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Labcontrol is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Labcontrol. If not, see <http://www.gnu.org/licenses/>.
*/
#include "mainwindow.h"
#include <QtGlobal>
#include <QInputDialog>
lcMainWindow::lcMainWindow( QWidget *argParent ) :
QMainWindow{ argParent },
icons{ new QPixmap[ icons_t::ICON_QUANTITY ] },
ui{ new Ui::MainWindow }
{
ui->setupUi( this );
lablib = new lcLablib{ ui->PTEDebugMessages, this };
LoadIconPixmaps();
SetupWidgets();
if ( valid_items ) {
gui_update_timer = new QTimer{ this };
connect(gui_update_timer, &QTimer::timeout, this, &lcMainWindow::UpdateClientsTableView );
gui_update_timer->start( 500 );
}
}
lcMainWindow::~lcMainWindow() {
delete ui;
delete valid_items;
delete[] icons;
}
bool lcMainWindow::CheckIfUserIsAdmin() {
// Query the current user's name or give an error if this fails
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
QString userName{ "" };
// For Linux
if ( env.contains( "USER" )) {
userName = env.value( "USER", "" );
}
// For Windows
if ( env.contains( "USERNAME" )) {
userName = env.value( "USERNAME", "" );
}
if ( userName == "" ) {
QMessageBox messageBox{ QMessageBox::Warning, tr( "User not detectable" ),
tr( "Your user name could not be queryed. The admin tab will be disabled. You won't be able to perform administrative actions but can conduct experiments normally." ), QMessageBox::Ok };
messageBox.exec();
return false;
}
ui->PTEDebugMessages->appendPlainText( tr( "[DEBUG] The user's name is %1." ).arg( userName ) );
lablib->SetUserNameOnServer( userName );
QStringList *adminUsers = lablib->GetAdminUsers();
if ( !( adminUsers == nullptr ) ) {
for ( auto s : *adminUsers ) {
if ( s == userName ) {
ui->PTEDebugMessages->appendPlainText( tr( "[DEBUG] '%1' has administrative rights." ).arg( userName ) );
return true;
}
}
}
return false;
}
void lcMainWindow::DisableDisfunctionalWidgets() {
// Disable all functions relying on the labcontrol installation directory if it is not available
if ( !lablib->GetSettingsItem( settingsItems_t::LABCONTROL_INSTALLATION_DIRECTORY ) ) {
ui->CBClientNames->setEnabled( false );
ui->CBWebcamChooser->setEnabled( false );
ui->GBClientActions->setEnabled( false );
ui->GBzTree->setEnabled( false );
ui->LEFilePath->setEnabled( false );
ui->LFakeName->setEnabled( false );
ui->LWebcamChooser->setEnabled( false );
ui->PBBeamFile->setEnabled( false );
ui->PBChooseFile->setEnabled( false );
ui->PBKillLocalzLeaf->setEnabled( false );
ui->PBRunzLeaf->setEnabled( false );
ui->PBShowPreprints->setEnabled( false );
ui->PBStartLocalzLeaf->setEnabled( false );
}
// Disable 'PBShowPreprints', if 'file_manager' was not set
if ( !lablib->GetSettingsItem( settingsItems_t::FILE_MANAGER ) ) {
ui->PBShowPreprints->setEnabled( false );
}
// Disable 'PBBoot', if 'network_broadcast_address' was not set
if ( !lablib->GetSettingsItem( settingsItems_t::NETWORK_BROADCAST_ADDRESS ) ) {
ui->PBBoot->setEnabled( false );
}
// Disable 'PBShowORSEE', if 'orsee_command' was not set
if ( !lablib->GetSettingsItem( settingsItems_t::ORSEE_COMMAND ) ) {
ui->PBShowORSEE->setEnabled( false );
}
// Disable all widgets needless if 'public_key_path_user' or 'user_name_on_clients' was not set
if ( !lablib->GetSettingsItem( settingsItems_t::PUBLICKEY_PATH_USER ) || !lablib->GetSettingsItem( settingsItems_t::USER_NAME_ON_CLIENTS ) ) {
ui->CBClientNames->setEnabled( false );
ui->LEFilePath->setEnabled( false );
ui->LFakeName->setEnabled( false );
ui->PBBeamFile->setEnabled( false );
ui->PBChooseFile->setEnabled( false );
ui->PBDeactivateScreensaver->setEnabled( false );
ui->PBKillzLeaf->setEnabled( false );
ui->PBRunzLeaf->setEnabled( false );
ui->PBShutdown->setEnabled( false );
ui->PBStartzLeaf->setEnabled( false );
ui->RBUseUserRoot->click();
ui->RBUseLocalUser->setEnabled( false );
}
// Disable widgets needless if 'public_key_path_root' was not set
if ( !lablib->GetSettingsItem( settingsItems_t::PUBLICKEY_PATH_ROOT ) ) {
ui->RBUseUserRoot->setEnabled( false );
ui->RBUseLocalUser->click();
}
if ( !lablib->GetSettingsItem( settingsItems_t::PUBLICKEY_PATH_ROOT ) && !lablib->GetSettingsItem( settingsItems_t::PUBLICKEY_PATH_USER ) ) {
ui->GBExecuteOnEveryClient->setEnabled( false );
ui->GBOptionsForAdminActions->setEnabled( false );
ui->PBOpenTerminal->setEnabled( false );
}
// Disable beam functionality if 'rcp_command' was not set
if ( !lablib->GetSettingsItem( settingsItems_t::RCP_COMMAND ) ) {
ui->LEFilePath->setEnabled( false );
ui->PBBeamFile->setEnabled( false );
ui->PBChooseFile->setEnabled( false );
}
// Disable 'PBRunzLeaf' and 'PBStartzLeaf' if 'server_ip' was not set
if ( !lablib->GetSettingsItem( settingsItems_t::SERVER_IP ) ) {
ui->PBRunzLeaf->setEnabled( false );
ui->PBStartzLeaf->setEnabled( false );
}
// Disable any actions concerning the clients if 'ssh_command' was not set
if ( !lablib->GetSettingsItem( settingsItems_t::SSH_COMMAND ) ) {
ui->CBClientNames->setEnabled( false );
ui->GBClientActions->setEnabled( false );
ui->LFakeName->setEnabled( false );
ui->LEFilePath->setEnabled( false );
ui->PBBeamFile->setEnabled( false );
ui->PBChooseFile->setEnabled( false );
ui->PBKillzLeaf->setEnabled( false );
ui->PBRunzLeaf->setEnabled( false );
ui->PBShutdown->setEnabled( false );
ui->PBStartzLeaf->setEnabled( false );
ui->TAdminActions->setEnabled( false );
}
// Disable 'PBOpenTerminal' if 'terminal_emulator_command' was not set
if ( !lablib->GetSettingsItem( settingsItems_t::TERMINAL_EMULATOR_COMMAND ) ) {
ui->GBExecuteOnEveryClient->setEnabled( false );
ui->PBOpenTerminal->setEnabled( false );
}
// Disable 'PBViewDesktop' if 'vnc_viewer' was not set
if ( !lablib->GetSettingsItem( settingsItems_t::VNC_VIEWER ) ) {
ui->PBViewDesktop->setEnabled( false );
}
// Disable 'PBBoot' if 'wakeonlan_command' was not set
if ( !lablib->GetSettingsItem( settingsItems_t::WAKEONLAN_COMMAND ) ) {
ui->PBBoot->setEnabled( false );
}
// Disable the disable screensaver function if the 'xset_command' was not set
if ( !lablib->GetSettingsItem( settingsItems_t::XSET_COMMAND ) ) {
ui->PBDeactivateScreensaver->setEnabled( false );
}
}
void lcMainWindow::LoadIconPixmaps() {
if ( !lablib->GetSettingsItem( settingsItems_t::LABCONTROL_INSTALLATION_DIRECTORY ) ) {
return;
}
const QStringList iconNames{ QStringList{}
<< "unknown.png"
<< "off.png"
<< "down.png"
<< "boot.png"
<< "on.png"
<< "zLeaf.png" };
for ( int i = 0; i < ( int )icons_t::ICON_QUANTITY; i++ ) {
if ( !icons[ i ].load( *lablib->GetSettingsItem( settingsItems_t::LABCONTROL_INSTALLATION_DIRECTORY ) + "/icons/" + iconNames[ i ] ) ) {
QMessageBox::information( this, tr( "Could not load icon '%1'" ).arg( iconNames[ i ] ),
tr( "The icon in '%1/icons/%2' could not be loaded." )
.arg( *lablib->GetSettingsItem( settingsItems_t::LABCONTROL_INSTALLATION_DIRECTORY ) ).arg( iconNames[ i ] ), QMessageBox::Ok );
}
}
}
void lcMainWindow::on_CBWebcamChooser_activated( int index ) {
if ( !( index == 0 ) ) {
QString program{ *lablib->GetSettingsItem( settingsItems_t::LABCONTROL_INSTALLATION_DIRECTORY ) + "/webcam_display" };
QStringList arguments;
arguments << ui->CBWebcamChooser->currentText();
QProcess showWebcamProcess;
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
showWebcamProcess.setProcessEnvironment( env );
showWebcamProcess.startDetached( program, arguments );
}
}
void lcMainWindow::on_PBBeamFile_clicked() {
QModelIndexList activatedItems = ui->TVClients->selectionModel()->selectedIndexes();
const QString * const publickeyPathUser = lablib->GetSettingsItem( settingsItems_t::PUBLICKEY_PATH_USER ), * const userNameOnClients = lablib->GetSettingsItem( settingsItems_t::USER_NAME_ON_CLIENTS );
const QString fileToBeam{ ui->LEFilePath->text() };
for ( QModelIndexList::ConstIterator it = activatedItems.cbegin(); it != activatedItems.cend(); ++it ) {
if ( ( *it ).data( Qt::DisplayRole ).type() != 0 ) {
lcClient *client = static_cast< lcClient * >( ( *it ).data( Qt::UserRole ).value< void * >() );
client->BeamFile( fileToBeam, publickeyPathUser, userNameOnClients );
}
}
}
void lcMainWindow::on_PBBoot_clicked() {
QModelIndexList activatedItems = ui->TVClients->selectionModel()->selectedIndexes();
for ( QModelIndexList::ConstIterator it = activatedItems.cbegin(); it != activatedItems.cend(); ++it ) {
if ( ( *it ).data( Qt::DisplayRole ).type() != 0 ) {
lcClient *client = static_cast< lcClient * >( ( *it ).data( Qt::UserRole ).value< void * >() );
client->Boot( lablib->GetSettingsItem( settingsItems_t::NETWORK_BROADCAST_ADDRESS ) );
}
}
}
void lcMainWindow::on_PBChooseFile_clicked() {
QFileDialog *file_dialog = new QFileDialog{ this, tr( "Choose a file to beam" ), QDir::homePath() };
file_dialog->setFileMode( QFileDialog::Directory );
file_dialog->setOption( QFileDialog::DontUseNativeDialog, true );
file_dialog->setOption( QFileDialog::ReadOnly, true );
file_dialog->setOption( QFileDialog::ShowDirsOnly, true );
if(file_dialog->exec()) {
ui->LEFilePath->setText(file_dialog->selectedFiles().at(0));
ui->PTEDebugMessages->appendPlainText( tr( "[DEBUG] Chose file '%1' for beaming." ).arg( ui->LEFilePath->text() ) );
}
else {
ui->LEFilePath->setText( tr( "File choosing cancelled" ) );
ui->PTEDebugMessages->appendPlainText( tr( "[DEBUG] File choosing cancelled" ) );
}
delete file_dialog;
}
void lcMainWindow::on_PBDeactivateScreensaver_clicked() {
const QString * const publickey_path_user = lablib->GetSettingsItem( settingsItems_t::PUBLICKEY_PATH_USER ), * const user_name_on_clients = lablib->GetSettingsItem( settingsItems_t::USER_NAME_ON_CLIENTS );
QVector< lcClient* > *clients = lablib->GetClients();
for ( auto s : *clients ) {
if ( s->GetClientState() >= state_t::RESPONDING )
s->DeactiveScreensaver( publickey_path_user, user_name_on_clients );
}
}
void lcMainWindow::on_PBExecute_clicked() {
// This will be set to false, if the command shall be executed only on the chosen clients (that's if not all clients are up)
bool executeOnEveryClient = true;
// Cancel, if not all clients are up and running
QVector< lcClient* > *clients = lablib->GetClients();
for ( auto s: *clients ) {
if ( !( s->name.contains( "backup", Qt::CaseInsensitive ) ) ) {
if ( s->GetClientState() < state_t::RESPONDING ) {
QMessageBox messageBox{ QMessageBox::Warning, tr( "Not all clients are running" ),
tr( "Not all clients are running. The command could not be executed on every client and should therefore be canceled to keep the clients consistent.\n\nAre you sure you want to continue only with the currently chosen clients?" ), QMessageBox::No | QMessageBox::Yes, this };
messageBox.setDefaultButton( QMessageBox::No );
messageBox.exec();
clients = nullptr;
executeOnEveryClient = false;
if ( messageBox.clickedButton() == messageBox.button( QMessageBox::No ) ) {
return;
} else {
break;
}
}
}
}
// Get the command to be executed ...
QString command = ui->CBCommandToExecute->currentText();
// Set the correct public key
QString *tempPublickeyPathUser = nullptr;
if ( ui->RBUseUserRoot->isChecked() ) {
tempPublickeyPathUser = lablib->GetSettingsItem( settingsItems_t::PUBLICKEY_PATH_ROOT );
} else {
tempPublickeyPathUser = lablib->GetSettingsItem( settingsItems_t::PUBLICKEY_PATH_USER );
}
const QString * const publickeyPathUser = tempPublickeyPathUser;
// and execute it
if ( executeOnEveryClient ) {
ui->PTEDebugMessages->appendPlainText( tr( "[DEBUG] Executing command '%1' on every client." ).arg( command ) );
for ( auto s: *clients ) {
if ( !( s->name.contains( "backup", Qt::CaseInsensitive ) ) ) {
s->OpenTerminal( command, ui->RBUseUserRoot->isChecked(), publickeyPathUser, lablib->GetSettingsItem( settingsItems_t::USER_NAME_ON_CLIENTS ) );
}
}
} else {
ui->PTEDebugMessages->appendPlainText( tr( "[DEBUG] Executing command '%1' only on chosen clients." ).arg( command ) );
QModelIndexList activated_items = ui->TVClients->selectionModel()->selectedIndexes();
for ( QModelIndexList::ConstIterator it = activated_items.cbegin(); it != activated_items.cend(); ++it ) {
if ( ( *it ).data( Qt::DisplayRole ).type() != 0 ) {
lcClient *client = static_cast< lcClient * >( ( *it ).data( Qt::UserRole ).value< void * >() );
client->OpenTerminal( command, ui->RBUseUserRoot->isChecked(), publickeyPathUser, lablib->GetSettingsItem( settingsItems_t::USER_NAME_ON_CLIENTS ) );
}
}
}
clients = nullptr;
}
void lcMainWindow::on_PBKillLocalzLeaf_clicked() {
QString program{ *lablib->GetSettingsItem( settingsItems_t::LABCONTROL_INSTALLATION_DIRECTORY ) + "/scripts/kill_zLeaf_labcontrol2.sh" };
// Start the process
QProcess kill_zleaf_process;
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
kill_zleaf_process.setProcessEnvironment( env );
kill_zleaf_process.startDetached( program );
local_zLeaves_are_running = false;
// Output message via the debug messages tab
ui->PTEDebugMessages->appendPlainText( "[DEBUG] " + program );
}
void lcMainWindow::on_PBKillzLeaf_clicked() {
QModelIndexList activated_items = ui->TVClients->selectionModel()->selectedIndexes();
const QString * const publickeyPathUser = lablib->GetSettingsItem( settingsItems_t::PUBLICKEY_PATH_USER ), * const userNameOnClients = lablib->GetSettingsItem( settingsItems_t::USER_NAME_ON_CLIENTS );
for ( QModelIndexList::ConstIterator it = activated_items.cbegin(); it != activated_items.cend(); ++it ) {
if ( ( *it ).data( Qt::DisplayRole ).type() != 0 ) {
lcClient *client = static_cast< lcClient * >( ( *it ).data( Qt::UserRole ).value< void * >() );
client->KillZLeaf( publickeyPathUser, userNameOnClients );
}
}
}
void lcMainWindow::on_PBOpenFilesystem_clicked() {
// Determine the correct user to be used
QString * userToBeUsed = nullptr;
if ( ui->RBUseUserRoot->isChecked() ) {
userToBeUsed = new QString{ "root" };
} else {
userToBeUsed = new QString{ *lablib->GetSettingsItem( settingsItems_t::USER_NAME_ON_CLIENTS ) };
}
QModelIndexList activated_items = ui->TVClients->selectionModel()->selectedIndexes();
for ( QModelIndexList::ConstIterator it = activated_items.cbegin(); it != activated_items.cend(); ++it ) {
if ( ( *it ).data( Qt::DisplayRole ).type() != 0 ) {
lcClient *client = static_cast< lcClient * >( ( *it ).data( Qt::UserRole ).value< void * >() );
client->OpenFilesystem( userToBeUsed );
}
}
delete userToBeUsed;
}
void lcMainWindow::on_PBOpenTerminal_clicked() {
QString *tempPublickeyPathUser = nullptr;
if ( ui->RBUseUserRoot->isChecked() ) {
tempPublickeyPathUser = lablib->GetSettingsItem( settingsItems_t::PUBLICKEY_PATH_ROOT );
} else {
tempPublickeyPathUser = lablib->GetSettingsItem( settingsItems_t::PUBLICKEY_PATH_USER );
}
const QString * const publickeyPathUser = tempPublickeyPathUser,
* const userNameOnClients = lablib->GetSettingsItem( settingsItems_t::USER_NAME_ON_CLIENTS );
QModelIndexList activated_items = ui->TVClients->selectionModel()->selectedIndexes();
for ( QModelIndexList::ConstIterator it = activated_items.cbegin(); it != activated_items.cend(); ++it ) {
if ( ( *it ).data( Qt::DisplayRole ).type() != 0 ) {
lcClient *client = static_cast< lcClient * >( ( *it ).data( Qt::UserRole ).value< void * >() );
client->OpenTerminal( QString{}, ui->RBUseUserRoot->isChecked(), publickeyPathUser, userNameOnClients );
}
}
}
void lcMainWindow::on_PBPrintPaymentFileManually_clicked() {
QFileDialog *fileDialog = new QFileDialog{ this, tr( "Please choose a payment file to print." ),
QDir::homePath(), "*.pay" };
fileDialog->setFileMode( QFileDialog::ExistingFile );
fileDialog->setOption( QFileDialog::ReadOnly, true );
if( fileDialog->exec() ) {
QString fileName = fileDialog->selectedFiles().at( 0 );
QString *dateString = new QString{ fileName.split( '/', QString::KeepEmptyParts, Qt::CaseInsensitive ).last()
.split( '.', QString::KeepEmptyParts, Qt::CaseInsensitive ).first() };
QString *workPath = new QString{ fileName };
workPath->truncate( workPath->lastIndexOf( '/' ) );
// lcReceiptsHandler *receiptsHandler = new lcReceiptsHandler{ ui->PTEDebugMessages, *workPath,
// ui->CBReceiptsforLocalClients->isChecked(), lablib->GetAnonymousReceiptsPlaceholder(),
// ui->CBReceiptsHeader->currentText(), lablib->GetSettingsItems(), dateString, this };
// connect( receiptsHandler, &lcReceiptsHandler::PrintingFinished, receiptsHandler, &lcReceiptsHandler::deleteLater );
}
delete fileDialog;
}
void lcMainWindow::on_PBRunzLeaf_clicked() {
// Show an error message, if no zTree version was chosen yet
if ( ui->CBzLeafVersion->currentIndex() == 0 ) {
QMessageBox messageBox{ QMessageBox::Warning, tr( "Unset z-Leaf version" ), tr( "There is no z-Leaf version chosen yet. Please choose one." ), QMessageBox::Ok, this };
messageBox.exec();
return;
}
// Check if more than one client is selected and issue a warning message if so
unsigned short int numberOfSelectedClients = 0;
QModelIndexList activatedItems = ui->TVClients->selectionModel()->selectedIndexes();
for ( QModelIndexList::ConstIterator it = activatedItems.cbegin(); it != activatedItems.cend(); ++it ) {
if ( ( *it ).data( Qt::DisplayRole ).type() != 0 ) {
++numberOfSelectedClients;
}
}
ui->PTEDebugMessages->appendPlainText( tr( "[DEBUG] %1 clients are selected.").arg( QString::number( numberOfSelectedClients ) ) );
if ( numberOfSelectedClients > 1 ) {
QMessageBox messageBox{ QMessageBox::Information, tr( "Too many clients selected" ), tr( "There are too many clients selected in the table view on the left. Please select only one." ), QMessageBox::Ok, this };
messageBox.exec();
} else {
const QString * const fakeName = new QString{ ui->CBClientNames->currentText() }, * const zLeafVersion = new QString{ ui->CBzLeafVersion->currentText() },
* const publickeyPathUser = lablib->GetSettingsItem( settingsItems_t::PUBLICKEY_PATH_USER ), * const serverIP = lablib->GetSettingsItem( settingsItems_t::SERVER_IP ),
* const userNameOnClients = lablib->GetSettingsItem( settingsItems_t::USER_NAME_ON_CLIENTS );
for ( QModelIndexList::ConstIterator it = activatedItems.cbegin(); it != activatedItems.cend(); ++it ) {
if ( ( *it ).data( Qt::DisplayRole ).type() != 0 ) {
lcClient *client = static_cast< lcClient * >( ( *it ).data( Qt::UserRole ).value< void * >() );
client->StartZLeaf( publickeyPathUser, userNameOnClients, zLeafVersion, serverIP, ui->SBzLeafPort->value(), fakeName );
}
}
delete fakeName;
delete zLeafVersion;
}
}
void lcMainWindow::on_PBShowORSEE_clicked() {
lablib->ShowOrsee();
}
void lcMainWindow::on_PBShowPreprints_clicked() {
lablib->ShowPreprints();
}
void lcMainWindow::on_PBShowSessions_clicked() {
QWidget *sessionDisplay = new lcSessionDisplay{ lablib->GetSessionsModel(), this };
sessionDisplay->setWindowFlags( Qt::Window );
sessionDisplay->show();
connect( sessionDisplay, &lcSessionDisplay::destroyed, sessionDisplay, &lcSessionDisplay::deleteLater );
}
void lcMainWindow::on_PBShutdown_clicked() {
QModelIndexList activatedItems = ui->TVClients->selectionModel()->selectedIndexes();
const QString * const publickeyPathUser = lablib->GetSettingsItem( settingsItems_t::PUBLICKEY_PATH_USER ),
* const userNameOnClients = lablib->GetSettingsItem( settingsItems_t::USER_NAME_ON_CLIENTS );
for ( QModelIndexList::ConstIterator it = activatedItems.cbegin(); it != activatedItems.cend(); ++it ) {
if ( ( *it ).data( Qt::DisplayRole ).type() != 0 ) {
lcClient *client = static_cast< lcClient * >( ( *it ).data( Qt::UserRole ).value< void * >() );
client->Shutdown( publickeyPathUser, userNameOnClients );
}
}
}
void lcMainWindow::on_PBStartLocalzLeaf_clicked() {
// Show an error message, if no z-Leaf version was chosen yet
if ( ui->CBzLeafVersion->currentIndex() == 0 ) {
QMessageBox::information( this, tr( "Unset z-Leaf version" ), tr( "There is no z-Leaf version chosen yet. Please choose one." ), QMessageBox::Ok );
return;
}
// Create a QMessageBox for user interaction if there is already a z-Leaf running
QMessageBox *messageBox = nullptr;
if ( local_zLeaves_are_running ) {
messageBox = new QMessageBox{ QMessageBox::Warning, tr( "Running local zLeaf found" ), tr( "There already seems to exist a local zLeaf instance" ), QMessageBox::No | QMessageBox::Yes, this };
messageBox->setInformativeText( "Do you want to start a local zLeaf nonetheless?" );
messageBox->setDefaultButton( QMessageBox::No );
messageBox->exec();
}
if ( ( messageBox != nullptr && messageBox->clickedButton() == messageBox->button( QMessageBox::Yes ) ) || !local_zLeaves_are_running ) {
// Ask for the name the local zLeaf shall have
QString name = QInputDialog::getText( this, tr( "The local zLeaf's name" ), tr( "Please enter the name the local zLeaf shall have." ),
QLineEdit::Normal, *lablib->GetLocalZLeafDefaultName() );
lablib->SetLocalZLeafDefaultName( name );
QString program = QString{ *lablib->GetSettingsItem( settingsItems_t::LABCONTROL_INSTALLATION_DIRECTORY ) + "/scripts/start_zLeaf_labcontrol2.sh" };
QStringList arguments;
arguments << ui->CBzLeafVersion->currentText() << "127.0.0.1" << QString::number( ui->SBzLeafPort->value() - 7000 ) << name;
// Start the process
QProcess start_zLeaf_process;
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
start_zLeaf_process.setProcessEnvironment( env );
start_zLeaf_process.startDetached( program, arguments );
local_zLeaves_are_running = true;
// Output message via the debug messages tab
ui->PTEDebugMessages->appendPlainText( "[DEBUG] " + program + " " + arguments.join( " " ) );
}
delete messageBox;
}
void lcMainWindow::on_PBStartzLeaf_clicked() {
// Show an error message, if no z-Leaf version was chosen yet
if ( ui->CBzLeafVersion->currentIndex() == 0 ) {
QMessageBox messageBox{ QMessageBox::Warning, tr( "Unset z-Leaf version" ), tr( "There is no z-Leaf version chosen yet. Please choose one." ), QMessageBox::Ok, this };
messageBox.exec();
return;
}
QModelIndexList activated_items = ui->TVClients->selectionModel()->selectedIndexes();
const QString * const publickeyPathUser = lablib->GetSettingsItem( settingsItems_t::PUBLICKEY_PATH_USER ),
* const serverIP = lablib->GetSettingsItem( settingsItems_t::SERVER_IP ),
* const userNameOnClients = lablib->GetSettingsItem( settingsItems_t::USER_NAME_ON_CLIENTS ),
* const zLeafVersion = new QString{ ui->CBzLeafVersion->currentText() };
for ( QModelIndexList::ConstIterator it = activated_items.cbegin(); it != activated_items.cend(); ++it ) {
if ( ( *it ).data( Qt::DisplayRole ).type() != 0 ) {
lcClient *client = static_cast< lcClient * >( ( *it ).data( Qt::UserRole ).value< void * >() );
client->StartZLeaf( publickeyPathUser, userNameOnClients, zLeafVersion, serverIP, ui->SBzLeafPort->value(), nullptr );
}
}
delete zLeafVersion;
}
void lcMainWindow::on_PBStartzTree_clicked() {
lcSessionStarter *sessionStarter = new lcSessionStarter{ lablib, ui->PTEDebugMessages, this };
sessionStarter->setWindowFlags( Qt::Window );
sessionStarter->show();
connect( sessionStarter, &lcSessionStarter::destroyed, sessionStarter, &lcSessionStarter::deleteLater );
// // Show an error message, if no zTree version was chosen yet
// if (ui->CBzTreeVersion->currentIndex() == 0) {
// QMessageBox messageBox{ QMessageBox::Warning, tr("Unset zTree version"), tr("There is no zTree version chosen yet. Please choose one."), QMessageBox::Ok, this };
// messageBox.exec();
// return;
// }
// // Ask a second time, if no valid LaTeX header was set
// if (ui->CBReceiptsHeader->currentText() == "None found") {
// QMessageBox messageBox{ QMessageBox::Information, tr("No valid LaTeX header chosen"),
// tr("No valid LaTeX header was chosen. Receipts creation and printing will not work. Shall a new zTree instance be started nonetheless?"), QMessageBox::Yes | QMessageBox::No, this };
// messageBox.exec();
// if (messageBox.clickedButton() == messageBox.button(QMessageBox::No))
// return;
// }
// // Show an error message, if there is already a zTree instance using the currently chosen port
// QVector<int> *occupiedPorts = lablib->GetOccupiedPorts();
// if ( occupiedPorts->length() != 0 ) {
// int chosenPort = ui->SBPort->value();
// for ( auto cit = occupiedPorts->cbegin(); cit != occupiedPorts->cend(); ++cit ) {
// if ( *cit == chosenPort ) {
// QMessageBox messageBox{ QMessageBox::Warning, tr( "Port is already used" ),
// tr( "The chosen port is already used by another started zTree instance.\nPlease choose another port." ), QMessageBox::Ok, this };
// messageBox.exec();
// occupiedPorts = nullptr;
// return;
// }
// }
// }
// occupiedPorts = nullptr;
// ui->CBDataTargetPath->setStyleSheet( "" );
// ui->CBPrintanonymousreceipts->setStyleSheet( "" );
// ui->CBReceiptsHeader->setStyleSheet( "" );
// ui->CBReceiptsforLocalClients->setStyleSheet( "" );
// ui->SBPort->setStyleSheet( "" );
// lablib->StartNewZTreeInstance();
}
void lcMainWindow::on_PBViewDesktop_clicked() {
QModelIndexList activatedItems = ui->TVClients->selectionModel()->selectedIndexes();
for ( QModelIndexList::ConstIterator it = activatedItems.cbegin(); it != activatedItems.cend(); ++it ) {
if ( ( *it ).data( Qt::DisplayRole ).type() != 0 ) {
lcClient *client = static_cast< lcClient * >( ( *it ).data( Qt::UserRole ).value< void * >() );
client->ShowDesktop();
}
}
}
void lcMainWindow::on_RBUseLocalUser_toggled(bool checked) {
if ( checked ) {
ui->PTEDebugMessages->appendPlainText( tr( "[DEBUG] RBUseLocalUser got toggled." ) );
}
}
void lcMainWindow::SetupWidgets() {
// Fill the 'CBClientNames' with possible client names and the 'TVClients' with the clients
const QVector< lcClient* > *clients = lablib->GetClients();
if ( !( clients == nullptr ) ) {
valid_items = new QVector< QStandardItem * >;
valid_items->reserve( clients->size() );
clients_view_model = new QStandardItemModel{ this };
for ( auto *s : *clients ) {
int temp_xpos = s->xPosition - 1, temp_ypos = s->yPosition - 1;
// Check if a client already exists at the given position and skip, if so
if ( clients_view_model->item( temp_ypos, temp_xpos ) ) {
QMessageBox::information( this, tr( "Double assignment to one position" ),
tr( "Two clients where set for the same position, '%1' will be dropped." ).arg( s->name ) );
continue;
}
// Work to be done for the 'CBClientNames'
ui->CBClientNames->addItem( s->name );
// Work to be done for the 'TVClients'
QStandardItem *item = new QStandardItem( s->name );
item->setBackground( QBrush( QColor( 127, 255, 127, 255 ) ) );
QVariant v = qVariantFromValue( static_cast< void * >( s ) );
item->setData( v, Qt::UserRole );
item->setIcon( icons[ ( int )icons_t::UNKNOWN ] );
clients_view_model->setItem( temp_ypos, temp_xpos, item );
valid_items->append( item );
}
ui->TVClients->setModel( clients_view_model );
valid_items->squeeze();
} else {
QMessageBox messageBox{ QMessageBox::Warning, tr( "Could not construct clients view" ),
tr( "The creation of the clients view failed. Please check the file '/etc/xdg/Economic Laboratory/Labcontrol.conf'." ), QMessageBox::Ok, this };
messageBox.exec();
ui->CBClientNames->setEnabled( false );
ui->GBClientActions->setEnabled( false );
ui->LEFilePath->setEnabled( false );
ui->LFakeName->setEnabled( false );
ui->PBBeamFile->setEnabled( false );
ui->PBChooseFile->setEnabled( false );
ui->PBRunzLeaf->setEnabled( false );
ui->TAdminActions->setEnabled( false );
ui->TVClients->setEnabled( false );
}
// Fill the 'CBWebcamChooser' with all available network webcams
ui->CBWebcamChooser->addItem( "Choose a webcam to view:", QVariant( 0 ) );
QStringList *webcams = lablib->GetWebcams();
if ( !( webcams == nullptr ) ) {
for ( auto s : *webcams )
ui->CBWebcamChooser->addItem( s, QVariant( 0 ) );
}
// Append available client webcams to the 'CBWebcamChooser'
if ( !( clients == nullptr ) ) {
for ( auto *s : *clients ) {
if ( s->hasWebcam )
ui->CBWebcamChooser->addItem( s->name, QVariant( 1 ) );
}
}
// Deactivate the webcam choosing interface, if no webcams are available (none mounted at clients and none mounted in the laboratory)
if ( ui->CBWebcamChooser->count() == 1 ) {
ui->LWebcamChooser->setEnabled( false );
ui->CBWebcamChooser->setEnabled( false );
QMessageBox messageBox{ QMessageBox::Warning, tr( "Could not add webcams" ),
tr( "The initialization of the webcams failed. Please check the configuration file of Labcontrol." ), QMessageBox::Ok, this };
messageBox.exec();
}
clients = nullptr;
webcams = nullptr;
const QStringList *zTreeEntries = lablib->GetInstalledZTreeVersions();
if ( zTreeEntries == nullptr ) {
ui->CBClientNames->setEnabled( false );
ui->GBzTree->setEnabled( false );
ui->GLzLeafSettings->setEnabled( false );
ui->LFakeName->setEnabled( false );
ui->PBRunzLeaf->setEnabled( false );
ui->PBStartLocalzLeaf->setEnabled( false );
ui->PBStartzLeaf->setEnabled( false );
} else {
ui->CBzLeafVersion->addItem( "NONE" );
for ( auto zTreeVersionString : *zTreeEntries ) {
ui->CBzLeafVersion->addItem( zTreeVersionString );
}
ui->CBzLeafVersion->setCurrentIndex( 0 );
}
// Disable the admin tab if the user has no administrative rights and set it up
if ( CheckIfUserIsAdmin() ) {
ui->TAdminActions->setEnabled( true );
ui->LAdministrativeRights->setText( tr( "You have administrative rights." ) );
} else {
ui->LAdministrativeRights->setText( tr( "You don't have administrative rights." ) );
}
ui->LUserName->setText( tr( "You are user %1" ).arg( lablib->GetUserNameOnServer() ) );
if ( lablib->GetSettingsItem( settingsItems_t::USER_NAME_ON_CLIENTS ) ) {
ui->RBUseLocalUser->setText( *lablib->GetSettingsItem( settingsItems_t::USER_NAME_ON_CLIENTS ) );
} else {
ui->RBUseUserRoot->click();
}
// Match the QRadioButtons correct groups
userChooseButtonGroup = new QButtonGroup{ this };
userChooseButtonGroup->setExclusive( true );
userChooseButtonGroup->addButton( ui->RBUseLocalUser );
userChooseButtonGroup->addButton( ui->RBUseUserRoot );
ui->RBUseLocalUser->click();
// Fill the CBCommandToExecute QComboBox
if ( lablib->GetSettingsItem( settingsItems_t::USER_NAME_ON_CLIENTS ) ) {
ui->CBCommandToExecute->addItems( QStringList{} << "" << "apt update" << "apt full-upgrade -y" << "reboot" << "rm -rfv /home/" + *lablib->GetSettingsItem( settingsItems_t::USER_NAME_ON_CLIENTS ) + "/.mozilla" << "uname -a" );
} else {
ui->CBCommandToExecute->addItems( QStringList{} << "" << "apt update" << "apt full-upgrade -y" << "reboot" << "uname -a" );
}
DisableDisfunctionalWidgets();
const QString date{ __DATE__ };
const QString time{ __TIME__ };
// Set the info text in LInfo on the TInfo tab
ui->LInfo->setText( "This is Labcontrol.\n\nCopyright 2014-2015 Markus Prasser\n\n\n"
"Labcontrol is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation"
", either version 3 of the License, or\n(at your option) any later version.\n\n"
"Labcontrol is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\nSee the GNU General Public License for more details.\n\n"
"You should have received a copy of the GNU General Public License\nalong with Labcontrol. If not, see <http://www.gnu.org/licenses/>.\n\n\n"
"This version of Labcontrol was compiled on " + date + " at " + time + "." );
}
void lcMainWindow::UpdateClientsTableView() {
for ( auto s : *valid_items ) {
state_t state = static_cast< lcClient * >( s->data( Qt::UserRole ).value<void *>() )->GetClientState();
switch ( state ) {
case state_t::RESPONDING:
s->setBackground( QBrush( QColor( 128, 255, 128, 255 ) ) );
s->setIcon( icons[ ( int )icons_t::ON ] );
break;
case state_t::NOT_RESPONDING:
s->setBackground( QBrush( QColor( 255, 255, 128, 255 ) ) );
s->setIcon( icons[ ( int )icons_t::OFF ] );
break;
case state_t::BOOTING:
s->setBackground( QBrush( QColor( 128, 128, 255, 255 ) ) );
s->setIcon( icons[ ( int )icons_t::BOOT ] );
break;
case state_t::SHUTTING_DOWN:
s->setBackground( QBrush( QColor( 128, 128, 255, 255 ) ) );
s->setIcon( icons[ ( int )icons_t::DOWN ] );
break;
case state_t::ZLEAF_RUNNING:
s->setBackground( QBrush( QColor( 0, 255, 0, 255 ) ) );
s->setIcon( icons[ ( int )icons_t::ZLEAF ] );
break;
case state_t::UNINITIALIZED:
case state_t::ERROR:
s->setBackground( QBrush( QColor( 255, 128, 128, 255 ) ) );
break;
}
}
}

@ -0,0 +1,112 @@
/*
* Copyright 2014-2016 Markus Prasser
*
* This file is part of Labcontrol.
*
* Labcontrol is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Labcontrol is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Labcontrol. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
enum class icons_t : unsigned short int { UNKNOWN, OFF, DOWN, BOOT, ON, ZLEAF, ICON_QUANTITY };
#include "Lib/client.h"
#include "Lib/lablib.h"
#include "sessionstarter.h"
#include "sessiondisplay.h"
#include "ui_mainwindow.h"
#include <cmath>
#include <QDir>
#include <QFileDialog>
#include <QMainWindow>
#include <QSettings>
#include <QStandardItemModel>
#include <QThread>
#include <QTimer>
#include <QVector>
namespace Ui {
class MainWindow;
}
//! The class containing the graphical user interface.
/*!
This class represents the graphical user interface and all connected functionality.
*/
class lcMainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit lcMainWindow( QWidget *argParent = 0 );
~lcMainWindow();
private slots:
void on_CBWebcamChooser_activated(int index);
void on_PBBeamFile_clicked();
void on_PBBoot_clicked();
void on_PBChooseFile_clicked();
void on_PBDeactivateScreensaver_clicked();
void on_PBExecute_clicked();
void on_PBKillLocalzLeaf_clicked();
void on_PBKillzLeaf_clicked();
void on_PBOpenFilesystem_clicked();
void on_PBOpenTerminal_clicked();
void on_PBPrintPaymentFileManually_clicked();
void on_PBRunzLeaf_clicked();
void on_PBShowORSEE_clicked();
void on_PBShowPreprints_clicked();
void on_PBShowSessions_clicked();
void on_PBShutdown_clicked();
void on_PBStartLocalzLeaf_clicked();
void on_PBStartzLeaf_clicked();
void on_PBStartzTree_clicked();
void on_PBViewDesktop_clicked();
void on_RBUseLocalUser_toggled(bool checked);
//! Updates the icons of the QTableView displaying the clients' states
/*!
* This function iterates over all valid items of the 'TVClients' and sets their states
* by querying the represented 'lcClient' instances.
*/
void UpdateClientsTableView();
signals:
private:
//! Checks, if the user has administrative rights
/*!
@return Returns true, if the user has administrative rights (If the user is in the 'sudo' group)
*/
bool CheckIfUserIsAdmin();
//! Disables widgets for functions not available due to lacking devices or settings
void DisableDisfunctionalWidgets();
//! Loads all needed client icon QPixmaps
void LoadIconPixmaps();
//! Sets up all used widgets
void SetupWidgets();
QStandardItemModel *clients_view_model = nullptr; //! The view storing all clients data
QTimer *gui_update_timer = nullptr; //! A QTimer triggering updates of the graphical user interface
QPixmap *icons = nullptr; //! Array of pixmaps storing the icons indicating the clients' statuses
lcLablib *lablib = nullptr; //! Accumulator of all program logic being accessed by the GUI
bool local_zLeaves_are_running = false; //! Stores if a local zLeaf instance is running on the server ('true' if local zLeaf exists)
QButtonGroup *userChooseButtonGroup = nullptr; //! Used to group the radio buttons choosing which user shall be used for administrative client actions
Ui::MainWindow *ui = nullptr; //! Pointer storing all GUI items
QVector<QStandardItem *> *valid_items = nullptr; //! Stores all valid Client instances displayed by the table view, its main use is as iterable object for 'update_clients_table_view()'
};
#endif // MAINWINDOW_H

@ -0,0 +1,518 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>960</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>800</width>
<height>960</height>
</size>
</property>
<property name="windowTitle">
<string>Labcontrol</string>
</property>
<property name="windowIcon">
<iconset>
<normaloff>../../../../../../usr/share/labcontrol/icons/icon.png</normaloff>../../../../../../usr/share/labcontrol/icons/icon.png</iconset>
</property>
<widget class="QWidget" name="centralWidget">
<layout class="QVBoxLayout" name="verticalLayout_8">
<item>
<widget class="QTabWidget" name="TWExperimenterTab">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>600</height>
</size>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="TExperimenterActions">
<attribute name="title">
<string>Experimenter actions</string>
</attribute>
<layout class="QHBoxLayout" name="HLExperimenterTab" stretch="1,1">
<item>
<layout class="QVBoxLayout" name="VLzTreeClientActions">
<item>
<widget class="QGroupBox" name="GBzTree">
<property name="title">
<string>z-Tree</string>
</property>
<property name="flat">
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QPushButton" name="PBStartzTree">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Start zTree with the set parameters.&lt;br/&gt;&lt;br/&gt;Take care, that all options in the 'z-Tree' and the 'Receipts' boxes must be set BEFORE starting the accordant z-Tree, since they cannot be changed anymore after the start.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Start z-Tree</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="PBShowSessions">
<property name="text">
<string>Show Sessions</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="PBPrintPaymentFileManually">
<property name="toolTip">
<string>Choose a payment file manually, which will then be printed.</string>
</property>
<property name="text">
<string>Print payment file manually</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="GBClientActions">
<property name="title">
<string>Client actions</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QPushButton" name="PBBoot">
<property name="toolTip">
<string>Boots the selected clients.</string>
</property>
<property name="text">
<string>Boot</string>
</property>
</widget>
</item>
<item>
<layout class="QGridLayout" name="GLzLeafSettings">
<item row="0" column="0">
<widget class="QLabel" name="LzLeafVersion">
<property name="toolTip">
<string>The z-Leaf version which shall be used for the experiment.</string>
</property>
<property name="text">
<string>z-Leaf version:</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QComboBox" name="CBzLeafVersion">
<property name="toolTip">
<string>The z-Leaf version which shall be used for the experiment.</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="LzLeafPort">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The port which will be used by started z-Leaf instances. Only matters if multiple experiments shall be run in parallel.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Port</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="SBzLeafPort">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The port which will be used by started z-Leaf instances. Only matters if multiple experiments shall be run in parallel.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="minimum">
<number>7000</number>
</property>
<property name="maximum">
<number>65535</number>
</property>
<property name="value">
<number>7000</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QPushButton" name="PBStartzLeaf">
<property name="toolTip">
<string>Starts zLeaf on the selected clients with the port given in the 'zTree' groupbox.</string>
</property>
<property name="text">
<string>Start z-Leaf</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="PBKillzLeaf">
<property name="toolTip">
<string>Kills zLeaf on all selected clients.</string>
</property>
<property name="text">
<string>Kill z-Leaf</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="PBShutdown">
<property name="toolTip">
<string>Turns off the selected clients.</string>
</property>
<property name="text">
<string>Shutdown</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="PBViewDesktop">
<property name="toolTip">
<string>Shows the desktop of the selected clients.</string>
</property>
<property name="text">
<string>View desktop</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="VLFurtherSpecialActions">
<property name="spacing">
<number>6</number>
</property>
<item>
<widget class="QGroupBox" name="GBFurtherLocalActions">
<property name="title">
<string>Further local actions</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QPushButton" name="PBShowORSEE">
<property name="text">
<string>Show ORSEE</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="PBShowPreprints">
<property name="text">
<string>Show preprints</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="HLLocalzLeaf">
<item>
<widget class="QPushButton" name="PBStartLocalzLeaf">
<property name="text">
<string>Start local z-Leaf</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="PBKillLocalzLeaf">
<property name="text">
<string>Kill local z-Leaf</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="GBSpecialActions">
<property name="title">
<string>Special actions</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="QLabel" name="LWebcamChooser">
<property name="text">
<string>Show webcam:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="CBWebcamChooser"/>
</item>
<item>
<widget class="QPushButton" name="PBDeactivateScreensaver">
<property name="toolTip">
<string>Deactivates the screensavers on the selected clients.</string>
</property>
<property name="text">
<string>Deactivate screensaver</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="LFakeName">
<property name="enabled">
<bool>true</bool>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Run zLeaf with another name than the client's.&lt;br/&gt;Choose the name the zLeaf shall have:&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="scaledContents">
<bool>false</bool>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="CBClientNames"/>
</item>
<item>
<widget class="QPushButton" name="PBRunzLeaf">
<property name="text">
<string>Run z-Leaf</string>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="VLFileBeamer">
<item>
<layout class="QHBoxLayout" name="HLFileChooser">
<item>
<widget class="QPushButton" name="PBChooseFile">
<property name="text">
<string>Choose file</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="LEFilePath">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QPushButton" name="PBBeamFile">
<property name="text">
<string>Beam file to ~/media4zTree</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="TAdminActions">
<property name="enabled">
<bool>false</bool>
</property>
<attribute name="title">
<string>Admin actions</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<widget class="QGroupBox" name="GBInformation">
<property name="title">
<string>Information</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_7">
<item>
<widget class="QLabel" name="LUserName">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="LAdministrativeRights">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="GBOptionsForAdminActions">
<property name="title">
<string>Options for admin actions</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_10">
<item>
<layout class="QVBoxLayout" name="VLUseUser">
<item>
<widget class="QLabel" name="LUseUser">
<property name="text">
<string>User to be used for remote terminal session:</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="HLUserRBs">
<item>
<widget class="QRadioButton" name="RBUseUserRoot">
<property name="text">
<string>root</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="RBUseLocalUser">
<property name="text">
<string>Client's user</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="GBActionsWithSelectedClients">
<property name="title">
<string>Actions with selected clients</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QPushButton" name="PBOpenTerminal">
<property name="text">
<string>Open terminal</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="PBOpenFilesystem">
<property name="text">
<string>Open filesystem</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="GBExecuteOnEveryClient">
<property name="title">
<string>Execute command on every client</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_11">
<item>
<widget class="QComboBox" name="CBCommandToExecute">
<property name="editable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="PBExecute">
<property name="text">
<string>Execute</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="TDebugMessages">
<attribute name="title">
<string>Debug messages</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QPlainTextEdit" name="PTEDebugMessages">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="TInfo">
<property name="layoutDirection">
<enum>Qt::RightToLeft</enum>
</property>
<attribute name="title">
<string>Info</string>
</attribute>
<layout class="QVBoxLayout" name="VLInfo">
<item>
<widget class="QLabel" name="LInfo">
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<widget class="QTableView" name="TVClients">
<property name="minimumSize">
<size>
<width>0</width>
<height>256</height>
</size>
</property>
<property name="showGrid">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QStatusBar" name="statusBar"/>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections/>
</ui>

@ -0,0 +1,33 @@
/*
* Copyright 2014-2016 Markus Prasser
*
* This file is part of Labcontrol.
*
* Labcontrol is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Labcontrol is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Labcontrol. If not, see <http://www.gnu.org/licenses/>.
*/
#include "sessiondisplay.h"
#include "ui_sessiondisplay.h"
lcSessionDisplay::lcSessionDisplay( QAbstractTableModel *argSessionsModel, QWidget *argParent ) :
QWidget{ argParent },
ui{ new Ui::SessionDisplay }
{
ui->setupUi( this );
ui->TVSessions->setModel( argSessionsModel );
}
lcSessionDisplay::~lcSessionDisplay() {
delete ui;
}

@ -0,0 +1,42 @@
/*
* Copyright 2014-2016 Markus Prasser
*
* This file is part of Labcontrol.
*
* Labcontrol is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Labcontrol is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Labcontrol. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SESSIONDISPLAY_H
#define SESSIONDISPLAY_H
#include <QAbstractTableModel>
#include <QWidget>
namespace Ui {
class SessionDisplay;
}
class lcSessionDisplay : public QWidget
{
Q_OBJECT
public:
explicit lcSessionDisplay( QAbstractTableModel *argSessionsModel, QWidget *argParent = nullptr );
~lcSessionDisplay();
private:
Ui::SessionDisplay *ui = nullptr;
};
#endif // SESSIONDISPLAY_H

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SessionDisplay</class>
<widget class="QWidget" name="SessionDisplay">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Session Display</string>
</property>
<layout class="QVBoxLayout" name="VLSessionDisplay">
<item>
<widget class="QTableView" name="TVSessions"/>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

@ -0,0 +1,149 @@
/*
* Copyright 2014-2016 Markus Prasser
*
* This file is part of Labcontrol.
*
* Labcontrol is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Labcontrol is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Labcontrol. If not, see <http://www.gnu.org/licenses/>.
*/
#include "sessionstarter.h"
#include "ui_sessionstarter.h"
lcSessionStarter::lcSessionStarter( lcLablib *argLablib, QPlainTextEdit *argDebugMessagesTextEdit, QWidget *parent ) :
QWidget{ parent },
debugMessagesTextEdit { argDebugMessagesTextEdit },
lablib{ argLablib },
ui{ new Ui::lcSessionStarter }
{
ui->setupUi( this );
this->SetupWidgets();
}
lcSessionStarter::~lcSessionStarter() {
delete ui;
}
void lcSessionStarter::GetNewDataTargetPath() {
QFileDialog *file_dialog = new QFileDialog{ this };
file_dialog->setFileMode( QFileDialog::Directory );
file_dialog->setDirectory( QDir::homePath() );
file_dialog->setOption( QFileDialog::ShowDirsOnly, true );
file_dialog->setOption( QFileDialog::DontUseNativeDialog, true );
if( file_dialog->exec() ) {
QString file_name = file_dialog->selectedFiles().at( 0 );
ui->CBDataTargetPath->addItem( file_name );
ui->CBDataTargetPath->setCurrentText( file_name );
ui->CBDataTargetPath->activated( file_name );
}
delete file_dialog;
}
void lcSessionStarter::on_CBDataTargetPath_activated(const QString &arg1) {
if ( ui->CBDataTargetPath->currentIndex() == 0 ) {
emit NewDataTargetPathRequested();
return;
}
ui->CBDataTargetPath->setStyleSheet( "" );
lablib->SetChosenZTreeDataTargetPath( arg1 );
}
void lcSessionStarter::on_CBReceiptsHeader_activated(const QString &arg1) {
ui->CBReceiptsHeader->setStyleSheet( "" );
lablib->SetChosenLaTeXHeader( arg1 );
}
void lcSessionStarter::on_CBReplaceParticipantName_currentTextChanged(const QString &arg1) {
lablib->SetAnonymousReceiptsPlaceholder( arg1 );
}
void lcSessionStarter::on_CBzTreeVersion_activated(const QString &arg1) {
ui->CBzTreeVersion->setStyleSheet( "" );
lablib->SetChosenZTreeVersion( arg1 );
}
void lcSessionStarter::on_ChBPrintanonymousreceipts_clicked( bool checked ) {
// Enable or disable the corresponding widgets
if ( checked ) {
ui->LReplaceParticipantName->setEnabled( true );
ui->CBReplaceParticipantName->setEnabled( true );
ui->CBReplaceParticipantName->setStyleSheet( "background: cyan" );
// Pass the currently chosen placeholder
lablib->SetAnonymousReceiptsPlaceholder( ui->CBReplaceParticipantName->currentText() );
} else {
ui->LReplaceParticipantName->setEnabled( false );
ui->CBReplaceParticipantName->setEnabled( false );
ui->CBReplaceParticipantName->setStyleSheet( "" );
// Pass an empty QString as signal, that receipts shall not be anonymous
lablib->SetAnonymousReceiptsPlaceholder( "" );
}
ui->ChBPrintanonymousreceipts->setStyleSheet( "" );
}
void lcSessionStarter::on_ChBReceiptsforLocalClients_clicked( bool checked ) {
ui->ChBReceiptsforLocalClients->setStyleSheet( "" );
lablib->SetPrintReceiptsForLocalClients( checked );
}
void lcSessionStarter::on_SBPort_editingFinished() {
ui->SBPort->setStyleSheet( "" );
lablib->SetChosenZTreePort( ui->SBPort->value() );
}
void lcSessionStarter::SetupWidgets() {
ui->SBPort->setValue( lablib->GetChosenZTreePort() );
// Fill the 'CBzTreeVersion' combobox with known entries from the lablib class
ui->CBzTreeVersion->addItem( "NONE" );
const QStringList * const zTreeEntries = lablib->GetInstalledZTreeVersions();
if ( !( zTreeEntries == nullptr ) ) {
for ( auto zTreeVersionString : *zTreeEntries ) {
ui->CBzTreeVersion->addItem( zTreeVersionString );
}
ui->CBzTreeVersion->setCurrentIndex( 0 );
} else {
throw lcForbiddenCall{};
}
// Fill the 'CBReceipts' combobox with known entries from the lablib class
const QStringList *laTeXHeaders = lablib->GetInstalledLaTeXHeaders();
if ( laTeXHeaders ) {
if ( ( laTeXHeaders->count() == 1 ) && ( laTeXHeaders->at(0) == "None found" ) ) {
ui->GBReceipts->setEnabled( false );
}
ui->CBReceiptsHeader->addItems( *laTeXHeaders );
if ( laTeXHeaders->length() - 1 < lablib->GetDefaultReceiptIndex() ) {
QMessageBox::information( this, tr( "'default_receipt_index' to high" ),
tr( "'default_receipt_index' was set to big. The combo box containing the receipt templates will default to the first entry." ) );
debugMessagesTextEdit->appendPlainText( tr("'default_receipt_index' was set to big. The combo box containing the receipt templates will default to the first entry." ) );
ui->CBReceiptsHeader->setCurrentIndex( 0 );
} else {
ui->CBReceiptsHeader->setCurrentIndex( lablib->GetDefaultReceiptIndex() );
}
laTeXHeaders = nullptr;
}
// Fill the 'CBDataTargetPath' combobox with some data target path examples
ui->CBDataTargetPath->addItem( tr( "Set new path" ) );
ui->CBDataTargetPath->addItem( QDir::homePath() );
ui->CBDataTargetPath->addItem( QString{ QDir::homePath() + "/zTreeData" } );
ui->CBDataTargetPath->setCurrentIndex( 2 );
connect( this, &lcSessionStarter::NewDataTargetPathRequested, this, &lcSessionStarter::GetNewDataTargetPath );
// Since filling a QComboBox does not emit the 'activated' signal, initially set some variables manually
lablib->SetChosenLaTeXHeader( ui->CBReceiptsHeader->currentText() );
lablib->SetChosenZTreeDataTargetPath( ui->CBDataTargetPath->currentText() );
// Set the initial status of CBReceiptsforLocalClients according to the settings in lcLablib
ui->ChBReceiptsforLocalClients->setChecked( lablib->GetPrintReceiptsForLocalClients() );
}

@ -0,0 +1,67 @@
/*
* Copyright 2014-2016 Markus Prasser
*
* This file is part of Labcontrol.
*
* Labcontrol is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Labcontrol is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Labcontrol. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SESSIONSTARTER_H
#define SESSIONSTARTER_H
#include "src/Lib/lablib.h"
#include <QFileDialog>
#include <QWidget>
namespace Ui {
class lcSessionStarter;
}
class lcSessionStarter : public QWidget
{
Q_OBJECT
public:
explicit lcSessionStarter( lcLablib *argLablib, QPlainTextEdit *argDebugMessagesTextEdit, QWidget *parent = nullptr );
~lcSessionStarter();
//! This gets thrown as an exception if this class is created even if it shouldn't
//! (because no installed z-Tree instances could be detected).
class lcForbiddenCall {};
private:
QPlainTextEdit * const debugMessagesTextEdit = nullptr;
lcLablib * const lablib = nullptr;
Ui::lcSessionStarter *ui;
void SetupWidgets();
private slots:
//! Opens a 'QFileDialog' and tries to choose a new zTree data target path
void GetNewDataTargetPath();
void on_CBDataTargetPath_activated(const QString &arg1);
void on_CBReceiptsHeader_activated(const QString &arg1);
void on_CBReplaceParticipantName_currentTextChanged(const QString &arg1);
void on_CBzTreeVersion_activated(const QString &arg1);
void on_ChBPrintanonymousreceipts_clicked( bool checked );
void on_ChBReceiptsforLocalClients_clicked(bool checked);
void on_SBPort_editingFinished();
signals:
//! This signal becomes emitted if "Set new path" was chosen in the 'CBDataTargetPath'
void NewDataTargetPathRequested();
};
#endif // SESSIONSTARTER_H

@ -0,0 +1,222 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>lcSessionStarter</class>
<widget class="QWidget" name="lcSessionStarter">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>512</width>
<height>256</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>512</width>
<height>256</height>
</size>
</property>
<property name="windowTitle">
<string>Session Starter</string>
</property>
<layout class="QVBoxLayout" name="VLSessionStarter">
<item>
<layout class="QHBoxLayout" name="HLForBothGroupBoxes">
<item>
<widget class="QGroupBox" name="GBzTree">
<property name="title">
<string>z-Tree</string>
</property>
<layout class="QVBoxLayout" name="VLzTreeGroupBox">
<item>
<widget class="QLabel" name="LzTreeVersion">
<property name="toolTip">
<string>The zTree version which shall be used for the experiment.</string>
</property>
<property name="text">
<string>z-Tree version:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="CBzTreeVersion">
<property name="toolTip">
<string>The zTree version which shall be used for the experiment.</string>
</property>
<property name="styleSheet">
<string notr="true">background: cyan</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="LDataTargetPath">
<property name="toolTip">
<string>The path where zTree shall store its data.
Please take care that it does not contain any spaces or other special characters.</string>
</property>
<property name="text">
<string>Data target path:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="CBDataTargetPath">
<property name="toolTip">
<string>The path where zTree shall store its data.
Please take care that it does not contain any spaces or other special characters.</string>
</property>
<property name="styleSheet">
<string notr="true">background: cyan</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="ChBRamdiskForGamesafeFile">
<property name="enabled">
<bool>false</bool>
</property>
<property name="toolTip">
<string>This function is not implemented yet.</string>
</property>
<property name="text">
<string>Use ramdisk for gamesafe file</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="HLPort">
<item>
<widget class="QLabel" name="LPort">
<property name="toolTip">
<string>The port which will be used by started zTree and zLeaf instances. Only matters if multiple experiments shall be run in parallel.</string>
</property>
<property name="text">
<string>Port:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="SBPort">
<property name="toolTip">
<string>The port which will be used by started zTree and zLeaf instances. Only matters if multiple experiments shall be run in parallel.</string>
</property>
<property name="styleSheet">
<string notr="true">background: cyan</string>
</property>
<property name="minimum">
<number>7000</number>
</property>
<property name="maximum">
<number>65535</number>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="GBReceipts">
<property name="title">
<string>Receipts</string>
</property>
<layout class="QVBoxLayout" name="VLReceiptsGroupBox">
<item>
<widget class="QLabel" name="LReceiptsLabel">
<property name="text">
<string>Template for receipts:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="CBReceiptsHeader">
<property name="styleSheet">
<string notr="true">background: cyan</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="ChBPrintanonymousreceipts">
<property name="styleSheet">
<string notr="true">background: cyan</string>
</property>
<property name="text">
<string>Print anonymous receipts</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="LReplaceParticipantName">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Substitute participant names with:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="CBReplaceParticipantName">
<property name="enabled">
<bool>false</bool>
</property>
<property name="editable">
<bool>true</bool>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<item>
<property name="text">
<string>\hspace{5cm}</string>
</property>
</item>
<item>
<property name="text">
<string>anonym</string>
</property>
</item>
<item>
<property name="text">
<string>anonymous</string>
</property>
</item>
<item>
<property name="text">
<string>nicht ausfüllen</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QCheckBox" name="ChBReceiptsforLocalClients">
<property name="text">
<string>Print receipts for local clients</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QPushButton" name="PBStartzTree">
<property name="toolTip">
<string>Start zTree with the set parameters.
Take care, that all options in the 'z-Tree' and the 'Receipts' boxes must be set BEFORE starting the accordant z-Tree, since they cannot be changed anymore after the start.</string>
</property>
<property name="text">
<string>Start z-Tree</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
Loading…
Cancel
Save