summaryrefslogtreecommitdiffstats
path: root/modules-available/roomplanner/inc/pvsgenerator.inc.php
blob: c00d54392f80636897990b4d6a719362e62c49c8 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
<?php

define('X', 0);
define('Y', 1);

class PvsGenerator
{

	public static function generate()
	{

		if (!Module::isAvailable('locations')) {
			die('sorry, but the locations module is required');
		}


		/* get all rooms */
		$rooms = array();
		$ret = Database::simpleQuery(
			'SELECT l.locationid, l.parentlocationid, l.locationname, lr.locationid AS notnull, lr.managerip, lr.tutoruuid, m.clientip as tutorip '
			. 'FROM location l '
			. 'LEFT JOIN location_roomplan lr ON (l.locationid = lr.locationid)'
			. 'LEFT JOIN machine m ON (lr.tutoruuid = m.machineuuid)');
		while ($row = $ret->fetch(PDO::FETCH_ASSOC)) {
			$row['locationname'] = str_replace(',', ';', $row['locationname']); // comma probably not the best sep here
			settype($row['locationid'], 'int');
			settype($row['parentlocationid'], 'int');
			$rooms[$row['locationid']] = $row;
		}
		foreach ($rooms as &$room) {
			if ($room['parentlocationid'] > 0 && isset($rooms[$room['parentlocationid']])) {
				$rooms[$room['parentlocationid']]['skip'] = true; // Don't just unset, might be wrong order
			}
		}
		unset($room); // refd!

		/* collect names and build room blocks - filter empty rooms while at it */
		$roomNames = array();
		$roomBlocks = '';
		foreach ($rooms as $room) {
			if (is_null($room['notnull']) || isset($room['skip']) // Not leaf
				|| empty($room['managerip'])) // rooms without managerips don't make sense
				continue;
			$roomBlock = PvsGenerator::generateRoomBlock($room);
			if ($roomBlock === false)
				continue; // Room nonexistent or empty
			$roomNames[] = md5($room['locationname']);
			$roomBlocks .= $roomBlock;
		}

		/* output room plus [General]-block */
		return "[General]\n"
		. 'rooms=' . implode(', ', $roomNames) . "\n"
		. "allowClientQuit=False\n" // TODO: configurable
		. "showLockDesktopButton=True\n" // TODO: Make this configurable (or not)
		. "\n\n"
		. $roomBlocks;
	}

	/**
	 * Generate .ini section for specific room.
	 *
	 * @param $room array room/location data as fetched from db
	 * @return string|bool .ini section for room, or false if room is empty
	 */
	private static function generateRoomBlock($room)
	{
		$out = '[' . md5($room['locationname']) . "]\n";


		/* find all clients in that room */
		$machines = PvsGenerator::getMachines($room['locationid']);
		if (empty($machines))
			return false;

		$out .= "name=" . $room['locationname'] . "\n";

		/* manager */
		$mgr = $room['managerip'];
		$tutor = $room['tutorip'];
		if ($mgr) {
			$out .= 'mgrIP=' . $mgr . "\n";
		}
		/* tutor */
		if ($tutor) {
			$out .= 'tutorIP=' . $tutor . "\n";
		}

		/* grid */
		$out .= PvsGenerator::generateGrid($machines);

		return $out . "\n";
	}

	/**
	 * Generate grid size information and client position data for given clients.
	 *
	 * @param $machines array list of clients
	 * @return string grid and position data as required for a room's .ini section
	 */
	private static function generateGrid($machines)
	{
		$out = "";

		/* this is a virtual grid, we first need this to do some optimizations */
		$grid = array();
		/* add each contained client with position and ip */
		foreach ($machines as $machine) {
			$grid[$machine['clientip']] = [$machine['gridCol'], $machine['gridRow']];
		}
		/* find bounding box */
		PvsGenerator::boundingBox($grid, $minX, $minY, $maxX, $maxY);
		$clientSizeX = 4; /* TODO: optimize */
		$clientSizeY = 4; /* TODO: optimize */
		$sizeX = max($maxX - $minX + $clientSizeX, 1); /* never negative */
		$sizeY = max($maxY - $minY + $clientSizeY, 1); /* and != 0 to avoid divide-by-zero in pvsmgr */

		/* zoom all clients into bounding box */
		foreach ($grid as $ip => $pos) {
			$newX = $grid[$ip][X] - $minX;
			$newY = $grid[$ip][Y] - $minY;
			$grid[$ip] = [$newX, $newY];
		}

		$out .= "gridSize=@Size($sizeX $sizeY)\n";
		$out .= "clientSize=@Size($clientSizeX $clientSizeY)\n";
		$out .= "client\\size=" . count($grid) . "\n";

		$i = 1;
		foreach ($grid as $ip => $pos) {
			$out .= "client\\" . $i . "\\ip=$ip\n";
			$out .= "client\\" . $i++ . "\\pos=@Point(" . $pos[X] . ' ' . $pos[Y] . ")\n";
		}

		return $out;

	}

	/**
	 * Get all clients for given room with IP and position.
	 *
	 * @param $roomid int locationid of room
	 * @return array
	 */
	private static function getMachines($roomid)
	{
		$ret = Database::simpleQuery(
			'SELECT clientip, position FROM machine WHERE fixedlocationid = :locationid',
			['locationid' => $roomid]);

		$machines = array();

		while ($row = $ret->fetch(PDO::FETCH_ASSOC)) {
			$position = json_decode($row['position'], true);

			if ($position === false || !isset($position['gridRow']) || !isset($position['gridCol']))
				continue; // TODO: Remove entry/set to NULL?

			$machine = array();
			$machine['clientip'] = $row['clientip'];
			$machine['gridRow'] = $position['gridRow'];
			$machine['gridCol'] = $position['gridCol'];
			$machine['tutor'] = false; /* TODO: find out if machine is default tutor */
			$machine['manager'] = false; /* TODO: find out if machine is manager */

			$machines[] = $machine;
		}

		return $machines;

	}

	private static function boundingBox($grid, &$minX, &$minY, &$maxX, &$maxY)
	{
		$minX = PHP_INT_MAX; /* PHP_INT_MIN is only avaiable since PHP 7 */
		$maxX = ~PHP_INT_MAX;
		$minY = PHP_INT_MAX;
		$maxY = ~PHP_INT_MAX;

		foreach ($grid as $pos) {
			$minX = min($minX, $pos[X]);
			$maxX = max($maxX, $pos[X]);
			$minY = min($minY, $pos[Y]);
			$maxY = max($maxY, $pos[Y]);
		}
	}

}