File: ModelsManager.cpp

package info (click to toggle)
dyssol 1.4.0-2
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 18,188 kB
  • sloc: cpp: 53,643; sh: 85; python: 55; makefile: 12
file content (444 lines) | stat: -rw-r--r-- 13,456 bytes parent folder | download
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
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
/* Copyright (c) 2020, Dyssol Development Team. All rights reserved. This file is part of Dyssol. See LICENSE file for license information. */

#include "ModelsManager.h"
#include "DynamicUnit.h"
#include "FileSystem.h"
#include "DyssolStringConstants.h"
#include "ContainerFunctions.h"
#ifdef _MSC_VER
#else
#include <dlfcn.h>
#endif

CModelsManager::SModelDir::SModelDir(std::filesystem::path _path, std::string _key, bool _active)
	: path{ std::move(_path) }
	, key{ std::move(_key) }
	, active{ _active }
{
	// using empty path can cause problems on Linux, so check it explicitly here
	const std::filesystem::path path1 = !path.empty() ? absolute(path) : "";
	const std::filesystem::path path2 = std::filesystem::absolute(FileSystem::ExecutableDirPath() + L"/" + path.wstring());
	if (exists(path1))
		pathFull = path1;
	else if (exists(path2))
		pathFull = path2;
	else
		pathFull = path;
}

size_t CModelsManager::DirsNumber() const
{
	return m_dirsList.size();
}

bool CModelsManager::AddDir(const std::filesystem::path& _path, bool _active)
{
	const auto& it = std::find_if(m_dirsList.begin(), m_dirsList.end(), [&](const SModelDir& entry) { return entry.path == _path; });
	if (it != m_dirsList.end()) return false;	// this path has been already added
	m_dirsList.emplace_back(_path, StringFunctions::GenerateUniqueKey(AllDirsKeys()), _active);
	UpdateAvailableModels();
	return true;
}

bool CModelsManager::RemoveDir(size_t _index)
{
	if (_index >= m_dirsList.size()) return false;
	m_dirsList.erase(m_dirsList.begin() + _index);
	UpdateAvailableModels();
	return true;
}

bool CModelsManager::UpDir(size_t _index)
{
	if (_index >= m_dirsList.size() || _index == 0) return false;
	std::iter_swap(m_dirsList.begin() + _index, m_dirsList.begin() + _index - 1);
	UpdateAvailableModels();
	return true;
}

bool CModelsManager::DownDir(size_t _index)
{
	if (_index >= m_dirsList.size() || _index == m_dirsList.size() - 1) return false;
	std::iter_swap(m_dirsList.begin() + _index, m_dirsList.begin() + _index + 1);
	UpdateAvailableModels();
	return true;
}

std::filesystem::path CModelsManager::GetDirPath(size_t _index) const
{
	return _index < m_dirsList.size() ? m_dirsList[_index].path : "";
}

std::vector<std::filesystem::path> CModelsManager::GetAllActiveDirPaths() const
{
	std::vector<std::filesystem::path> res;
	for (const auto& p : m_dirsList)
		if (p.active)
			res.push_back(p.path);
	return res;
}

std::vector<std::filesystem::path> CModelsManager::GetAllActiveDirFullPaths() const
{
	std::vector<std::filesystem::path> res;
	for (const auto& p : m_dirsList)
		if (p.active)
			res.push_back(p.pathFull);
	return res;
}

bool CModelsManager::GetDirActivity(size_t _index) const
{
	return _index < m_dirsList.size() ? m_dirsList[_index].active : false;
}

void CModelsManager::SetDirActivity(size_t _index, bool _active)
{
	if (_index >= m_dirsList.size()) return;
	m_dirsList[_index].checked = m_dirsList[_index].active || !_active;
	m_dirsList[_index].active = _active;
	UpdateAvailableModels();
}

void CModelsManager::Clear()
{
	m_dirsList.clear();
	m_availableUnits.clear();
	m_availableSolvers.clear();
}

std::vector<SUnitDescriptor> CModelsManager::GetAvailableUnits() const
{
	return m_availableUnits;
}

std::vector<SSolverDescriptor> CModelsManager::GetAvailableSolvers() const
{
	return m_availableSolvers;
}

SSolverDescriptor CModelsManager::GetSolverDescriptor(const std::wstring& _fileName) const
{
	const std::wstring sLibName = FileSystem::FileName(_fileName);
	const auto res = std::find_if(m_availableSolvers.begin(), m_availableSolvers.end(), [&](const SSolverDescriptor& s) { return s.fileLocation.filename() == sLibName; });
	if (res == m_availableSolvers.end()) return {};
	return *res;
}

std::wstring CModelsManager::GetSolverLibName(const std::string& _key) const
{
	const auto res = std::find_if(m_availableSolvers.begin(), m_availableSolvers.end(), [&](const SSolverDescriptor& s) { return s.uniqueID == _key; });
	if (res == m_availableSolvers.end()) return {};
	return res->fileLocation.filename().wstring();
}

CBaseUnit* CModelsManager::InstantiateUnit(const std::string& _key)
{
	for (const auto& u : m_availableUnits)	// go through all available units
		if (u.uniqueID == _key)			// find the required descriptor
		{
			// load library
			const DYSSOL_LIBRARY_INSTANCE hLibrary = LoadDyssolLibrary(u.fileLocation);
			if (!hLibrary) return nullptr;
			// get constructor function
			const CreateUnit2 createUnitFunc = reinterpret_cast<CreateUnit2>(LoadDyssolLibraryConstructor(hLibrary, DYSSOL_CREATE_MODEL_FUN_NAME));
			if (!createUnitFunc)
			{
				CloseDyssolLibrary(hLibrary);
				continue; // seek further
			}
			// instantiate unit
			CBaseUnit* pUnit = createUnitFunc();
			if (!pUnit)
			{
				CloseDyssolLibrary(hLibrary);
				continue; // seek further
			}
			pUnit->CreateBasicInfo();
			// save created unit and its library
			m_loadedUnits[pUnit] = hLibrary;
			// return instantiated unit
			return pUnit;
		}
	return nullptr;
}

CBaseSolver* CModelsManager::InstantiateSolver(const std::string& _key)
{
	for (const auto& s : m_availableSolvers)	// go through all available solvers
		if (s.uniqueID == _key)				// find the required descriptor
		{
			// load library
			const DYSSOL_LIBRARY_INSTANCE hLibrary = LoadDyssolLibrary(s.fileLocation);
			if (!hLibrary) return nullptr;
			// get constructor function
			const CreateExternalSolver createSolverFunc = reinterpret_cast<CreateExternalSolver>(LoadDyssolLibraryConstructor(hLibrary, std::vector<std::string>{CREATE_SOLVER_FUN_NAMES}[static_cast<unsigned>(s.solverType)]));
			if (!createSolverFunc)
			{
				CloseDyssolLibrary(hLibrary);
				continue; // seek further
			}
			// instantiate solver
			CBaseSolver* pSolver = createSolverFunc();
			if (!pSolver)
			{
				CloseDyssolLibrary(hLibrary);
				continue; // seek further
			}
			pSolver->CreateBasicInfo();
			// save created solver and its library
			m_loadedSolvers[pSolver] = hLibrary;
			// return instantiated solver
			return pSolver;
		}
	return nullptr;
}

void CModelsManager::FreeUnit(CBaseUnit* _unit)
{
	if (!_unit) return;
	// test if such unit exists
	if (m_loadedUnits.find(_unit) == m_loadedUnits.end()) return;
	// copy entry
	const DYSSOL_LIBRARY_INSTANCE hLibrary = m_loadedUnits[_unit];
	// remove it from the list
	m_loadedUnits.erase(_unit);
	// delete unit
	delete _unit;
	_unit = nullptr;
	// close the library
	CloseDyssolLibrary(hLibrary);
}

void CModelsManager::FreeSolver(CBaseSolver* _solver)
{
	if (!_solver) return;
	// test if such solver exists
	if (m_loadedSolvers.find(_solver) == m_loadedSolvers.end()) return;
	// copy entry
	const DYSSOL_LIBRARY_INSTANCE hLibrary = m_loadedSolvers[_solver];
	// remove it from the list
	m_loadedSolvers.erase(_solver);
	// delete unit
	delete _solver;
	_solver = nullptr;
	// close the library
	CloseDyssolLibrary(hLibrary);
}

std::vector<std::string> CModelsManager::AllDirsKeys() const
{
	std::vector<std::string> res;
	for (const auto& dir : m_dirsList)
		res.push_back(dir.key);
	return res;
}

void CModelsManager::UpdateAvailableModels()
{
	// remove models laying in removed dirs
	const auto IsToDelete = [&](const SModelDescriptor& _model) -> bool // checks whether to delete model
	{
		for (const auto& dir : m_dirsList)
			if (dir.active && dir.key == _model.dirKey)
				return false;
		return true;
	};
	VectorDelete(m_availableUnits, IsToDelete);
	VectorDelete(m_availableSolvers, IsToDelete);

	// add models from added dirs
	for (auto& dir : m_dirsList)
		if (dir.active && !dir.checked)
		{
			// get all models from directory
			auto models = GetModelsList(dir.pathFull);
			// set directory key to all found models
			for (auto& unit : models.first)
				unit.dirKey = dir.key;
			for (auto& unit : models.second)
				unit.dirKey = dir.key;
			// add models to lists
			m_availableUnits.insert(m_availableUnits.end(), models.first.begin(), models.first.end());
			m_availableSolvers.insert(m_availableSolvers.end(), models.second.begin(), models.second.end());
			// set this directory as already checked
			dir.checked = true;
		}

	// sort models according to dirs order
	for (size_t i = 0; i < m_dirsList.size(); ++i)
	{
		for (auto& unit : m_availableUnits)
			if (unit.dirKey == m_dirsList[i].key)
				unit.position = i;
		for (auto& solver : m_availableSolvers)
			if (solver.dirKey == m_dirsList[i].key)
				solver.position = i;
	}
	std::sort(m_availableUnits.begin(), m_availableUnits.end());
	std::sort(m_availableSolvers.begin(), m_availableSolvers.end());
}

std::pair<std::vector<SUnitDescriptor>, std::vector<SSolverDescriptor>> CModelsManager::GetModelsList(const std::filesystem::path& _dir)
{
	// try to treat _dir as absolute path
	const std::filesystem::path absPath = std::filesystem::absolute(_dir);
	auto models = GetAllModelsInDir(absPath);
	if (models.first.empty() && models.second.empty())
	{
		// try to treat _dir as relative path
		const std::filesystem::path relPath = std::filesystem::absolute(FileSystem::ExecutableDirPath() + L"/" + _dir.wstring());
		models = GetAllModelsInDir(relPath);
	}
	return models;
}

std::pair<std::vector<SUnitDescriptor>, std::vector<SSolverDescriptor>> CModelsManager::GetAllModelsInDir(const std::filesystem::path& _dir)
{
	std::vector<SUnitDescriptor> resUnits;
	std::vector<SSolverDescriptor> resSolvers;
	for (const auto& f : FileSystem::FilesList(_dir, StrConst::MM_LibraryFileExtension))
		if(const DYSSOL_LIBRARY_INSTANCE lib = LoadDyssolLibrary(f))
		{
			if(const SUnitDescriptor unit = TryGetUnitDescriptor(f, lib))             // try to load unit from library
				resUnits.push_back(unit);
			else if (const SSolverDescriptor solver = TryGetSolverDescriptor(f, lib)) // try to load solver from library
				resSolvers.push_back(solver);
			CloseDyssolLibrary(lib);
		}
	return std::make_pair(resUnits, resSolvers);
}

SUnitDescriptor CModelsManager::TryGetUnitDescriptor(const std::filesystem::path& _pathToUnit, DYSSOL_LIBRARY_INSTANCE _library)
{
	// try to get constructor
	const CreateUnit2 createUnitFunc = reinterpret_cast<CreateUnit2>(LoadDyssolLibraryConstructor(_library, DYSSOL_CREATE_MODEL_FUN_NAME));
	if (!createUnitFunc)
		return {};

	// try to create unit
	CBaseUnit* pUnit;
	try {
		pUnit = createUnitFunc();
	}
	catch (const std::logic_error& e) {
		std::cerr << e.what() << std::endl;
		return {};
	}

	// do not allow code in constructor, as in previous versions
	try {
		if (!pUnit->GetUnitName().empty() || !pUnit->GetUniqueID().empty() || !pUnit->GetAuthorName().empty() || pUnit->GetVersion() != 0)
			return {};
	}
	catch (const std::logic_error& e) {
		std::cerr << e.what() << std::endl;
		return {};
	}

	try {
		pUnit->CreateBasicInfo();
		pUnit->DoCreateStructure();
	}
	catch (const std::logic_error& e) {
		std::cerr << e.what() << std::endl;
		return {};
	}

	SUnitDescriptor unitDescriptor;

	// obtain descriptor information
	try {
		unitDescriptor.uniqueID = pUnit->GetUniqueID();
		unitDescriptor.name = pUnit->GetUnitName();
		unitDescriptor.author = pUnit->GetAuthorName();
		unitDescriptor.version = pUnit->GetVersion();
		unitDescriptor.isDynamic = dynamic_cast<CDynamicUnit*>(pUnit);
		unitDescriptor.fileLocation = StringFunctions::UnifyPath(_pathToUnit);
	}
	catch (...) {
		unitDescriptor = {};
	}

	delete pUnit;
	return unitDescriptor;
}

SSolverDescriptor CModelsManager::TryGetSolverDescriptor(const std::filesystem::path& _pathToSolver, DYSSOL_LIBRARY_INSTANCE _library)
{
	SSolverDescriptor solverDescriptor;
	CBaseSolver* pSolver = nullptr;
	bool bFound = false;

	// go through all solver types
	for (size_t i = 1; i <= SOLVERS_TYPES_NUMBER && !bFound; ++i)
	{
		// try to get constructor
		const CreateExternalSolver createSolverFunc = reinterpret_cast<CreateExternalSolver>(LoadDyssolLibraryConstructor(_library, std::vector<std::string>{CREATE_SOLVER_FUN_NAMES}[i]));
		if (!createSolverFunc)
			continue;

		// try to create unit
		try {
			pSolver = createSolverFunc();
		}
		catch (const std::logic_error&) {
			continue;
		}

		// do not allow code in constructor, as in previous versions
		try {
			if (!pSolver->GetName().empty() || !pSolver->GetUniqueID().empty() || !pSolver->GetAuthorName().empty() || pSolver->GetVersion() != 0)
				continue;
		}
		catch (const std::logic_error&) {
			continue;
		}

		pSolver->CreateBasicInfo();

		// obtain descriptor information
		try {
			solverDescriptor.uniqueID = pSolver->GetUniqueID();
			solverDescriptor.name = pSolver->GetName();
			solverDescriptor.author = pSolver->GetAuthorName();
			solverDescriptor.version = pSolver->GetVersion();
			solverDescriptor.solverType = pSolver->GetType();
			solverDescriptor.fileLocation = StringFunctions::UnifyPath(_pathToSolver);
			bFound = true;
		}
		catch (...) {
			solverDescriptor = {};
		}
	}

	delete pSolver;
	return solverDescriptor;
}

DYSSOL_LIBRARY_INSTANCE CModelsManager::LoadDyssolLibrary(const std::filesystem::path& _libPath)
{
#ifdef _MSC_VER
	return LoadLibrary(_libPath.c_str());
#else
	return dlopen(_libPath.c_str(), RTLD_LAZY);
#endif
}

DYSSOL_CREATE_FUNCTION_TYPE CModelsManager::LoadDyssolLibraryConstructor(DYSSOL_LIBRARY_INSTANCE _lib, const std::string& _funName)
{
#ifdef _MSC_VER
	return GetProcAddress(_lib, _funName.c_str());
#else
	return dlsym(_lib, _funName.c_str());
#endif
}

void CModelsManager::CloseDyssolLibrary(DYSSOL_LIBRARY_INSTANCE _lib)
{
#ifdef _MSC_VER
	::FreeLibrary(_lib);
#else
	dlclose(_lib);
#endif
}