license changed to LGPL for libnxcl, libnxsnmp, libnxlp, libnxsl, and libnxmap
[public/netxms.git] / src / libnxmap / submap.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Network Maps Library
4 ** Copyright (C) 2003-2010 Victor Kirhenshtein
5 **
6 ** This program is free software; you can redistribute it and/or modify
7 ** it under the terms of the GNU Lesser General Public License as published by
8 ** the Free Software Foundation; either version 3 of the License, or
9 ** (at your option) any later version.
10 **
11 ** This program is distributed in the hope that it will be useful,
12 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 ** GNU General Public License for more details.
15 **
16 ** You should have received a copy of the GNU Lesser General Public License
17 ** along with this program; if not, write to the Free Software
18 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 **
20 ** File: submap.cpp
21 **
22 **/
23
24 #include "libnxmap.h"
25
26
27 //
28 // Default submap object constructor
29 //
30
31 nxSubmap::nxSubmap()
32 {
33 CommonInit();
34 }
35
36
37 //
38 // Create default submap for given object
39 //
40
41 nxSubmap::nxSubmap(DWORD dwObjectId)
42 {
43 CommonInit();
44 m_dwId = dwObjectId;
45 }
46
47
48 //
49 // Create submap object from NXCP message
50 //
51
52 nxSubmap::nxSubmap(CSCPMessage *pMsg)
53 {
54 CommonInit();
55 ModifyFromMessage(pMsg);
56 }
57
58
59 //
60 // Common initialization code
61 //
62
63 void nxSubmap::CommonInit(void)
64 {
65 m_dwId = 0;
66 m_dwAttr = SUBMAP_ATTR_AUTOMATIC_LAYOUT;
67 m_dwNumObjects = 0;
68 m_pObjectList = NULL;
69 m_dwNumLinks = 0;
70 m_pLinkList = NULL;
71 }
72
73
74 //
75 // Destructor
76 //
77
78 nxSubmap::~nxSubmap()
79 {
80 safe_free(m_pObjectList);
81 safe_free(m_pLinkList);
82 }
83
84
85 //
86 // Fill NXCP message with submap data
87 //
88
89 void nxSubmap::CreateMessage(CSCPMessage *pMsg)
90 {
91 DWORD i, j, dwId, *pdwList;
92
93 pMsg->SetVariable(VID_OBJECT_ID, m_dwId);
94 pMsg->SetVariable(VID_SUBMAP_ATTR, m_dwAttr);
95 pMsg->SetVariable(VID_NUM_OBJECTS, m_dwNumObjects);
96 if (m_dwNumObjects > 0)
97 {
98 pdwList = (DWORD *)malloc(sizeof(DWORD) * m_dwNumObjects * 3);
99 for(i = 0, j = 0; i < m_dwNumObjects; i++)
100 {
101 pdwList[j++] = m_pObjectList[i].dwId;
102 pdwList[j++] = m_pObjectList[i].x;
103 pdwList[j++] = m_pObjectList[i].y;
104 }
105 pMsg->SetVariableToInt32Array(VID_OBJECT_LIST, m_dwNumObjects * 3, pdwList);
106 free(pdwList);
107 }
108
109 pMsg->SetVariable(VID_NUM_LINKS, m_dwNumLinks);
110 if (m_dwNumLinks > 0)
111 {
112 pdwList = (DWORD *)malloc(sizeof(DWORD) * m_dwNumLinks * 3);
113 for(i = 0, j = 0, dwId = VID_SUBMAP_LINK_NAMES_BASE; i < m_dwNumLinks; i++)
114 {
115 pdwList[j++] = m_pLinkList[i].dwId1;
116 pdwList[j++] = m_pLinkList[i].dwId2;
117 pdwList[j++] = m_pLinkList[i].nType;
118 pMsg->SetVariable(dwId++, m_pLinkList[i].szPort1);
119 pMsg->SetVariable(dwId++, m_pLinkList[i].szPort2);
120 }
121 pMsg->SetVariableToInt32Array(VID_LINK_LIST, m_dwNumLinks * 3, pdwList);
122 free(pdwList);
123 }
124 }
125
126
127 //
128 // Modify submap object from NXCP message
129 //
130
131 void nxSubmap::ModifyFromMessage(CSCPMessage *pMsg)
132 {
133 DWORD i, j, dwId, *pdwList;
134
135 safe_free_and_null(m_pObjectList);
136 safe_free_and_null(m_pLinkList);
137
138 m_dwId = pMsg->GetVariableLong(VID_OBJECT_ID);
139 m_dwAttr = pMsg->GetVariableLong(VID_SUBMAP_ATTR) & 0xFFFF; // Clear run-time attributes
140
141 m_dwNumObjects = pMsg->GetVariableLong(VID_NUM_OBJECTS);
142 if (m_dwNumObjects > 0)
143 {
144 m_pObjectList = (MAP_OBJECT *)realloc(m_pObjectList, sizeof(MAP_OBJECT) * m_dwNumObjects);
145 pdwList = (DWORD *)malloc(sizeof(DWORD) * m_dwNumObjects * 3);
146 pMsg->GetVariableInt32Array(VID_OBJECT_LIST, m_dwNumObjects * 3, pdwList);
147 for(i = 0, j = 0; i < m_dwNumObjects; i++)
148 {
149 m_pObjectList[i].dwId = pdwList[j++];
150 m_pObjectList[i].x = pdwList[j++];
151 m_pObjectList[i].y = pdwList[j++];
152 m_pObjectList[i].dwState = 0;
153 }
154 free(pdwList);
155 }
156 else
157 {
158 safe_free_and_null(m_pObjectList);
159 }
160
161 m_dwNumLinks = pMsg->GetVariableLong(VID_NUM_LINKS);
162 if (m_dwNumLinks > 0)
163 {
164 m_pLinkList = (OBJLINK *)realloc(m_pLinkList, sizeof(OBJLINK) * m_dwNumLinks);
165 memset(m_pLinkList, 0, sizeof(OBJLINK) * m_dwNumLinks);
166 pdwList = (DWORD *)malloc(sizeof(DWORD) * m_dwNumLinks * 3);
167 pMsg->GetVariableInt32Array(VID_LINK_LIST, m_dwNumLinks * 3, pdwList);
168 for(i = 0, j = 0, dwId = VID_SUBMAP_LINK_NAMES_BASE; i < m_dwNumLinks; i++)
169 {
170 m_pLinkList[i].dwId1 = pdwList[j++];
171 m_pLinkList[i].dwId2 = pdwList[j++];
172 m_pLinkList[i].nType = pdwList[j++];
173 if ((m_pLinkList[i].nType < LINK_TYPE_NORMAL) ||
174 (m_pLinkList[i].nType > LINK_TYPE_VPN))
175 m_pLinkList[i].nType = LINK_TYPE_NORMAL;
176 pMsg->GetVariableStr(dwId++, m_pLinkList[i].szPort1, MAX_CONNECTOR_NAME);
177 pMsg->GetVariableStr(dwId++, m_pLinkList[i].szPort2, MAX_CONNECTOR_NAME);
178 }
179 free(pdwList);
180 }
181 else
182 {
183 safe_free_and_null(m_pLinkList);
184 }
185 }
186
187
188 //
189 // Set object's state
190 //
191
192 void nxSubmap::SetObjectState(DWORD dwObjectId, DWORD dwState)
193 {
194 DWORD i;
195
196 for(i = 0; i < m_dwNumObjects; i++)
197 if (m_pObjectList[i].dwId == dwObjectId)
198 {
199 m_pObjectList[i].dwState = dwState;
200 break;
201 }
202 }
203
204
205 //
206 // Set object's state
207 //
208
209 DWORD nxSubmap::GetObjectState(DWORD dwObjectId)
210 {
211 DWORD i, dwState = 0;
212
213 for(i = 0; i < m_dwNumObjects; i++)
214 if (m_pObjectList[i].dwId == dwObjectId)
215 {
216 dwState = m_pObjectList[i].dwState;
217 break;
218 }
219 return dwState;
220 }
221
222
223 //
224 // Get index of object with given ID
225 //
226
227 DWORD nxSubmap::GetObjectIndex(DWORD dwObjectId)
228 {
229 DWORD i;
230
231 for(i = 0; i < m_dwNumObjects; i++)
232 if (m_pObjectList[i].dwId == dwObjectId)
233 return i;
234 return INVALID_INDEX;
235 }
236
237
238 //
239 // Returns position of given object or (-1,-1) if object's position is undefined
240 //
241
242 POINT nxSubmap::GetObjectPosition(DWORD dwObjectId)
243 {
244 DWORD i;
245 POINT pt;
246
247 pt.x = -1;
248 pt.y = -1;
249 for(i = 0; i < m_dwNumObjects; i++)
250 if (m_pObjectList[i].dwId == dwObjectId)
251 {
252 pt.x = m_pObjectList[i].x;
253 pt.y = m_pObjectList[i].y;
254 break;
255 }
256 return pt;
257 }
258
259
260 //
261 // Returns position of given object or (-1,-1) if object's position is undefined
262 //
263
264 POINT nxSubmap::GetObjectPositionByIndex(DWORD dwIndex)
265 {
266 POINT pt;
267
268 if (dwIndex < m_dwNumObjects)
269 {
270 pt.x = m_pObjectList[dwIndex].x;
271 pt.y = m_pObjectList[dwIndex].y;
272 }
273 else
274 {
275 pt.x = -1;
276 pt.y = -1;
277 }
278 return pt;
279 }
280
281
282 //
283 // Set object's position
284 //
285
286 void nxSubmap::SetObjectPosition(DWORD dwObjectId, int x, int y)
287 {
288 DWORD i;
289
290 for(i = 0; i < m_dwNumObjects; i++)
291 if (m_pObjectList[i].dwId == dwObjectId)
292 {
293 m_pObjectList[i].x = x;
294 m_pObjectList[i].y = y;
295 break;
296 }
297 if (i == m_dwNumObjects)
298 {
299 // New element
300 m_dwNumObjects++;
301 m_pObjectList = (MAP_OBJECT *)realloc(m_pObjectList, m_dwNumObjects * sizeof(MAP_OBJECT));
302 m_pObjectList[i].dwId = dwObjectId;
303 m_pObjectList[i].x = x;
304 m_pObjectList[i].y = y;
305 m_pObjectList[i].dwState = 0;
306 }
307 }
308
309
310 //
311 // Set object's position using index
312 //
313
314 void nxSubmap::SetObjectPositionByIndex(DWORD dwIndex, int x, int y)
315 {
316 m_pObjectList[dwIndex].x = x;
317 m_pObjectList[dwIndex].y = y;
318 }
319
320
321 //
322 // Get minimal required size
323 //
324
325 POINT nxSubmap::GetMinSize(void)
326 {
327 POINT pt;
328 DWORD i;
329
330 pt.x = 0;
331 pt.y = 0;
332
333 for(i = 0; i < m_dwNumObjects; i++)
334 {
335 if (m_pObjectList[i].x > pt.x)
336 pt.x = m_pObjectList[i].x;
337 if (m_pObjectList[i].y > pt.y)
338 pt.y = m_pObjectList[i].y;
339 }
340
341 pt.x += MAP_OBJECT_SIZE_X + MAP_RIGHT_MARGIN;
342 pt.y += MAP_OBJECT_SIZE_Y + MAP_TEXT_BOX_HEIGHT + MAP_BOTTOM_MARGIN;
343
344 return pt;
345 }
346
347
348 //
349 // Layout objects on map
350 //
351
352 void nxSubmap::DoLayout(DWORD dwNumObjects, DWORD *pdwObjectList,
353 DWORD dwNumLinks, OBJLINK *pLinkList,
354 int nIdealX, int nIdealY, int nMethod,
355 BOOL bNormalize)
356 {
357 DWORD i;
358 int x, y;
359
360 safe_free(m_pLinkList);
361 m_dwNumLinks = dwNumLinks;
362 m_pLinkList = (pLinkList != NULL) ? (OBJLINK *)nx_memdup(pLinkList, sizeof(OBJLINK) * dwNumLinks) : NULL;
363
364 safe_free_and_null(m_pObjectList);
365 m_dwNumObjects = 0;
366
367 if (nMethod == SUBMAP_LAYOUT_DUMB)
368 {
369 for(i = 0, x = MAP_LEFT_MARGIN, y = MAP_TOP_MARGIN; i < dwNumObjects; i++)
370 {
371 SetObjectPosition(pdwObjectList[i], x, y);
372 x += MAP_OBJECT_SIZE_X + MAP_OBJECT_INTERVAL_X;
373 if (x >= nIdealX - MAP_OBJECT_SIZE_X - MAP_OBJECT_INTERVAL_X / 2)
374 {
375 x = MAP_LEFT_MARGIN;
376 y += MAP_OBJECT_SIZE_Y + MAP_TEXT_BOX_HEIGHT + MAP_OBJECT_INTERVAL_Y;
377 }
378 }
379 }
380 else
381 {
382 nxleGeneric *engine;
383 nxmap_Graph *graph;
384 nxmap_Vertex *vertex;
385
386 graph = new nxmap_Graph(dwNumObjects, pdwObjectList, dwNumLinks, pLinkList);
387 if (dwNumObjects > 0)
388 {
389 graph->SetRootVertex(pdwObjectList[0]);
390 if (bNormalize)
391 graph->NormalizeLinks();
392 }
393 switch(nMethod)
394 {
395 case SUBMAP_LAYOUT_RADIAL:
396 engine = new nxleRadial(graph);
397 break;
398 case SUBMAP_LAYOUT_REINGOLD_TILFORD:
399 engine = new nxleReingoldTilford(graph);
400 break;
401 default: // Unknown method, do nothing
402 engine = new nxleGeneric(graph);
403 break;
404 }
405
406 engine->Execute();
407 delete engine;
408
409 graph->NormalizeVertexPositions();
410 for(i = 0; i < dwNumObjects; i++)
411 {
412 vertex = graph->GetVertexByIndex(i);
413 SetObjectPosition(vertex->GetId(), vertex->GetPosX(), vertex->GetPosY());
414 }
415 delete graph;
416 }
417
418 m_dwAttr |= SUBMAP_ATTR_LAYOUT_COMPLETED;
419 }
420
421
422 //
423 // Add link between two objects
424 //
425
426 void nxSubmap::LinkObjects(DWORD dwObj1, const TCHAR *pszPort1, DWORD dwObj2, const TCHAR *pszPort2, int nType)
427 {
428 DWORD i;
429
430 // Validate object IDs
431 if ((GetObjectIndex(dwObj1) == INVALID_INDEX) ||
432 (GetObjectIndex(dwObj2) == INVALID_INDEX))
433 return; // At least one object not exist on submap
434
435 // Find if requested link already exist
436 for(i = 0; i < m_dwNumLinks; i++)
437 {
438 if (((m_pLinkList[i].dwId1 == dwObj1) && (m_pLinkList[i].dwId2 == dwObj2)) ||
439 ((m_pLinkList[i].dwId1 == dwObj2) && (m_pLinkList[i].dwId2 == dwObj1)))
440 break;
441 }
442 if (i == m_dwNumLinks)
443 {
444 // Create new link
445 m_dwNumLinks++;
446 m_pLinkList = (OBJLINK *)realloc(m_pLinkList, sizeof(OBJLINK) * m_dwNumLinks);
447 }
448 m_pLinkList[i].dwId1 = dwObj1;
449 m_pLinkList[i].dwId2 = dwObj2;
450 m_pLinkList[i].nType = nType;
451 nx_strncpy(m_pLinkList[i].szPort1, pszPort1, MAX_CONNECTOR_NAME);
452 nx_strncpy(m_pLinkList[i].szPort2, pszPort2, MAX_CONNECTOR_NAME);
453 }
454
455
456 //
457 // Remove link between two objects
458 //
459
460 void nxSubmap::UnlinkObjects(DWORD dwObj1, DWORD dwObj2)
461 {
462 DWORD i;
463
464 for(i = 0; i < m_dwNumLinks; i++)
465 {
466 if (((m_pLinkList[i].dwId1 == dwObj1) && (m_pLinkList[i].dwId2 == dwObj2)) ||
467 ((m_pLinkList[i].dwId1 == dwObj2) && (m_pLinkList[i].dwId2 == dwObj1)))
468 {
469 m_dwNumLinks--;
470 memmove(&m_pLinkList[i], &m_pLinkList[i + 1], sizeof(OBJLINK) * (m_dwNumLinks - i));
471 i--;
472 }
473 }
474 }
475
476
477 //
478 // Delete object from submap
479 //
480
481 void nxSubmap::DeleteObject(DWORD dwObject)
482 {
483 DWORD i;
484
485 // Delete all links to this object
486 for(i = 0; i < m_dwNumLinks; i++)
487 {
488 if ((m_pLinkList[i].dwId1 == dwObject) || (m_pLinkList[i].dwId2 == dwObject))
489 {
490 m_dwNumLinks--;
491 memmove(&m_pLinkList[i], &m_pLinkList[i + 1], sizeof(OBJLINK) * (m_dwNumLinks - i));
492 i--;
493 }
494 }
495
496 // Delete object itself
497 i = GetObjectIndex(dwObject);
498 if (i != INVALID_INDEX)
499 {
500 m_dwNumObjects--;
501 memmove(&m_pObjectList[i], &m_pObjectList[i + 1], sizeof(MAP_OBJECT) * (m_dwNumObjects - i));
502 }
503 }