프로젝트

일반

사용자정보

통계
| 브랜치(Branch): | 개정판:

hytos / HYTOS / HYTOS / Shapes / EngineeringConnectorItem.py @ 427ae4f8

이력 | 보기 | 이력해설 | 다운로드 (23.3 KB)

1
# coding: utf-8
2
""" This is engineering connector item module """
3

    
4
import os.path
5
import copy, sys
6

    
7
try:
8
    from PyQt5.QtCore import *
9
    from PyQt5.QtGui import *
10
    from PyQt5.QtWidgets import *
11
except ImportError:
12
    try:
13
        from PyQt4.QtCore import Qt, QRectF, pyqtSignal, QT_VERSION_STR
14
        from PyQt4.QtGui import QGraphicsView, QGraphicsScene, QImage, QPixmap, QPainterPath, QFileDialog
15
    except ImportError:
16
        raise ImportError("ImageViewerQt: Requires PyQt5 or PyQt4.")
17

    
18
from AppDocData import *
19
from EngineeringAbstractItem import QEngineeringAbstractItem
20

    
21

    
22
class NozzleData:
23
    """ this is nozzle data class """
24

    
25
    def __init__(self):
26
        """ constructor """
27
        self.pressure = None
28
        self.pressure_drop = None
29
        self.elevation = None
30
        self.over_design_cv = None
31
        self.cv_type = None
32
        self.desc = None
33
        self.pos = None
34

    
35
    def parse(self, row):
36
        """ parse given row """
37

    
38
        try:
39
            if not row['Pressure'] is None:
40
                self.pressure = float(row['Pressure'])
41
            if not row['Pressure_Drop'] is None:
42
                self.pressure_drop = float(row['Pressure_Drop'])
43
            if not row['Elevation'] is None:
44
                self.elevation = float(row['Elevation'])
45
            if not row['Over_Design_CV'] is None:
46
                self.over_design_cv = float(row['Over_Design_CV'])
47
            if not row['CV_Type'] is None:
48
                self.cv_type = row['CV_Type']
49
            if not row['Name'] is None:
50
                self.desc = row['Name']
51
            if not row['X'] is None and not row['Y'] is None:
52
                self.pos = (float(row['X']), float(row['Y']))
53

    
54
        except Exception as ex:
55
            from App import App
56
            from AppDocData import MessageType
57

    
58
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
59
                                                           sys.exc_info()[-1].tb_lineno)
60
            App.mainWnd().addMessage.emit(MessageType.Error, message)
61

    
62

    
63
"""
64
    A {ConnectorItem} is the graphical representation of a {Symbol.Connectors}.
65
"""
66

    
67

    
68
class QEngineeringConnectorItem(QGraphicsEllipseItem):
69
    """ This is engineering connector item class """
70
    SMALL_SIZE = 7
71
    BIG_SIZE = 11
72
    HIGHLIGHT = '#BC4438'
73

    
74
    AXIS_MODE = 0
75
    FREE_MODE = 1
76

    
77
    def __init__(self, uid=None, parent=None, index=-1):
78
        """constructor of connector"""
79
        import uuid
80
        from SymbolSvgItem import SymbolSvgItem
81
        from EngineeringEqpDescTextItem import QEngineeringEqpDescTextItem
82

    
83
        QGraphicsEllipseItem.__init__(self, parent)
84

    
85
        self.uid = uuid.uuid4() if uid is None else uuid.UUID(uid, version=4)
86
        self.parent = parent
87
        self.buildItem()
88
        self._direction = 'AUTO'
89
        self._symbol_idx = '0'
90
        self._connectedItem = None
91
        self._connected_at = QEngineeringAbstractItem.CONNECTED_AT_PT  # default value is connected at pt
92
        self.connectPoint = None
93
        self._hoverItem = None
94
        self._data = None
95

    
96
        self.setAcceptHoverEvents(True)
97
        self.transfer = Transfer()
98

    
99
        self._drawing_mode = QEngineeringConnectorItem.AXIS_MODE
100

    
101
        if type(parent) is SymbolSvgItem:
102
            """ setup label for connector index """
103
            self._font = QFont('Arial', QEngineeringConnectorItem.SMALL_SIZE)
104
            self._font.setPointSizeF(5)
105

    
106
        self._conn_index = index
107

    
108
    def __repr__(self):
109
        """ return string represent parent item and connector index """
110
        return '{}_{}_N{}'.format(self.parentItem().name, self.parentItem().index, self._conn_index)
111

    
112
    @staticmethod
113
    def find_connector(uid):
114
        """ find a connector which has given uid """
115
        from App import App
116
        import uuid
117

    
118
        res = None
119
        if type(uid) is uuid.UUID or type(uid) is str:
120
            scene = App.mainWnd().graphicsView.scene
121
            matches = [x for x in scene.items() if hasattr(x, 'uid') and str(x.uid) == str(uid)]
122
            res = matches[0] if matches else None
123

    
124
        return res
125

    
126
    @property
127
    def data(self):
128
        """ getter of nozzle data """
129
        return self._data
130

    
131
    @data.setter
132
    def data(self, value):
133
        """ setter of nozzle data """
134
        self._data = value
135

    
136
    @property
137
    def elevation(self):
138
        """ return elevation """
139
        return self.data.elevation
140

    
141
    @property
142
    def press_drop(self):
143
        return None
144

    
145
    @property
146
    def connectedItem(self):
147
        """ getter of connectedItem """
148
        import uuid
149

    
150
        if self.scene() and (type(self._connectedItem) is uuid.UUID or type(self._connectedItem) is str):
151
            matches = [x for x in self.scene().items() if hasattr(x, 'uid') and str(x.uid) == str(self._connectedItem)]
152
            if matches:
153
                self._connectedItem = matches[0]
154
            else:
155
                self._connectedItem = None
156

    
157
        return self._connectedItem
158

    
159
    @connectedItem.setter
160
    def connectedItem(self, value):
161
        """ setter of connectedItem """
162
        self._connectedItem = value
163

    
164
    '''
165
        @brief      getter of direction
166
        @author     humkyung
167
        @date       2018.09.01
168
    '''
169

    
170
    @property
171
    def direction(self):
172
        return self._direction
173

    
174
    '''
175
        @brief      setter of direction
176
        @author     humkyung
177
        @date       2018.09.01
178
    '''
179

    
180
    @direction.setter
181
    def direction(self, value):
182
        self._direction = value
183

    
184
    @property
185
    def symbol_idx(self):
186
        """ """
187
        return self._symbol_idx
188

    
189
    @symbol_idx.setter
190
    def symbol_idx(self, value):
191
        """ setter of symbol_idx """
192
        self._symbol_idx = value
193

    
194
    def validate(self):
195
        """validate connector's data"""
196
        from SymbolSvgItem import SymbolSvgItem
197
        from EngineeringStreamlineItem import QEngineeringStreamlineItem
198

    
199
        res = []
200

    
201
        try:
202
            parent = self.parentItem()
203
            if type(parent) is SymbolSvgItem:
204
                if parent.category == 'Equipment - [ Pressurized ]':
205
                    if self.connectedItem and \
206
                            (self.data is None or (self.data.pressure is None or self.data.elevation is None)):
207
                        res.extend([self, 'pressure or elevation is invalid'])
208
                elif parent.category == 'Equipment - [ Pressure Drop ]' or parent.type == 'Flowmeter':
209
                    if self.connectedItem and \
210
                            (self.data is None or (self.data.pressure_drop is None or self.data.elevation is None)):
211
                        res.extend([self, 'pressure drop or elevation is invalid'])
212
                elif parent.category == 'Equipment - [ Rotating ]':
213
                    if self.connectedItem and parent.name.upper() in ['L_KOMP', 'R_KOMP', 'R_PUMP', 'L_PUMP', 'V_PUMP']:
214
                        if self.data is None or self.data.elevation is None:
215
                            res.extend([self, 'need to check elevation'])
216
                    elif self.connectedItem and parent.name.upper() in ['L_COMP', 'R_COMP']:
217
                        if self.data is None or self.data.pressure is None:
218
                            res.extend([self, 'need to check pressure'])
219
                        elif self.data is None or self.data.elevation is None:
220
                            res.extend([self, 'need to check elevation'])
221
                    elif self.connectedItem and (self.data.pressure_drop is None or self.data.elevation is None):
222
                        res.extend([self, 'need to check pressure drop and elevation'])
223
                elif self.connectedItem and parent.name in ['CV_H', 'CV_V']:
224
                    if self.data is None or self.data.elevation is None:
225
                        res.extend([self, 'need to check elevation'])
226
                elif parent.name == 'Line Splitter':  # set default value for line splitter
227
                    if self.connectedItem:
228
                        if self.data.pressure_drop is None:
229
                            self.data.pressure_drop = 0
230
                        if self.data.elevation is None:
231
                            self.data.elevation = 0
232
            elif type(parent) is QEngineeringStreamlineItem:
233
                pass
234

    
235
            # update connector's color
236
            if self.connectedItem and type(self.parentItem()) is SymbolSvgItem:
237
                self.setBrush(Qt.red) if res else self.setBrush(Qt.yellow)
238
            else:
239
                self.setBrush(Qt.yellow) if self.connectedItem else self.setBrush(Qt.blue)
240
            # up to here
241
        except Exception as ex:
242
            from App import App
243
            from AppDocData import MessageType
244

    
245
            message = 'error occurred({}) in {}:{}'.format(repr(ex), sys.exc_info()[-1].tb_frame.f_code.co_filename,
246
                                                           sys.exc_info()[-1].tb_lineno)
247
            App.mainWnd().addMessage.emit(MessageType.Error, message)
248

    
249
        return res
250

    
251
    def dir(self):
252
        """return direction vector"""
253
        if self._direction == 'LEFT':
254
            return -1, 0
255
        elif self._direction == 'RIGHT':
256
            return 1, 0
257
        elif self._direction == 'UP':
258
            return 0, -1
259
        elif self._direction == 'DOWN':
260
            return 0, 1
261

    
262
        return None
263

    
264
    def buildItem(self):
265
        """build connector item"""
266
        from EngineeringStreamlineItem import QEngineeringStreamlineItem
267

    
268
        self.setBrush(Qt.blue)
269
        if self.parentItem() is not None:
270
            self.setZValue(self.parentItem().zValue() + 1)
271

    
272
    '''
273
        @brief      return center of connector
274
        @author     humkyung
275
        @date       2018.07.23
276
    '''
277

    
278
    def center(self):
279
        pt = self.sceneBoundingRect().center()
280
        return (pt.x(), pt.y())
281

    
282
    '''
283
        @brief  set position of connector
284
        @author humkyung
285
        @date   2018.05.02
286
    '''
287

    
288
    def setPos(self, pos):
289
        self._loc = [pos[0], pos[1]]
290
        self.setRect(self._loc[0] - round(self.SMALL_SIZE * 0.5), self._loc[1] - round(self.SMALL_SIZE * 0.5),
291
                     self.SMALL_SIZE, self.SMALL_SIZE)
292
        self.update()
293

    
294
        if hasattr(self, '_label'):
295
            """ set label positon at center of connector """
296
            self._label.setPos(QPointF(self._loc[0] - self._label.boundingRect().width() * 0.5,
297
                                       self._loc[1] - self._label.boundingRect().height() * 0.5))
298

    
299
    def connect(self, item, at=QEngineeringAbstractItem.CONNECTED_AT_PT):
300
        """connect to given item where given position"""
301
        from SymbolSvgItem import SymbolSvgItem
302

    
303
        if item:
304
            self.connectedItem = item
305
            self._connected_at = at
306
            if isinstance(item.parentItem(), SymbolSvgItem):
307
                item.parentItem().transfer.on_pos_changed.connect(self.parentItem().on_symbol_pos_changed)
308
        else:
309
            if isinstance(self.parentItem(), SymbolSvgItem):
310
                self.parentItem().transfer.on_pos_changed.disconnect(self.connectedItem.parentItem().on_symbol_pos_changed)
311

    
312
            self.connectedItem = None
313

    
314
        self.validate()
315
        self.update()
316

    
317
    def hoverEnterEvent(self, event):
318
        """highlight connector"""
319
        from EngineeringStreamlineItem import QEngineeringStreamlineItem
320

    
321
        try:
322
            if type(self.parentItem()) is not QEngineeringStreamlineItem:
323
                items = self.scene().items(event.scenePos())
324
                matched = [item for item in items if type(item.parentItem()) is QEngineeringStreamlineItem]
325
                if matched:
326
                    matched[0].hoverEnterEvent(event)
327
                else:
328
                    self.highlight(True)
329
            else:
330
                self.highlight(True)
331
        finally:
332
            QApplication.setOverrideCursor(Qt.CrossCursor)
333

    
334
    def hoverLeaveEvent(self, event):
335
        """unhighlight connector"""
336
        try:
337
            self.highlight(False)
338
        finally:
339
            QApplication.restoreOverrideCursor()
340

    
341
    def highlight(self, flag):
342
        """highlight or unhighlight"""
343

    
344
        if flag:
345
            self.setRect(self._loc[0] - round(self.BIG_SIZE * 0.5), self._loc[1] - round(self.BIG_SIZE * 0.5),
346
                         self.BIG_SIZE, self.BIG_SIZE)
347
            c = QColor()
348
            c.setNamedColor(QEngineeringConnectorItem.HIGHLIGHT)
349
            self.setPen(Qt.red)
350
            # self.setBrush(c)
351
            if hasattr(self, '_label'):
352
                font = self._label.font()
353
                font.setBold(True)
354
                self._label.setFont(font)
355
            self.parentItem().highlight(flag)
356
        else:
357
            self.setRect(self._loc[0] - round(self.SMALL_SIZE * 0.5), self._loc[1] - round(self.SMALL_SIZE * 0.5),
358
                         self.SMALL_SIZE, self.SMALL_SIZE)
359
            self.setPen(Qt.black)
360
            # self.setBrush(Qt.yellow) if self.connectedItem else self.setBrush(Qt.blue)
361
            if hasattr(self, '_label'):
362
                font = self._label.font()
363
                font.setBold(False)
364
                self._label.setFont(font)
365

    
366
            self.parentItem().highlight(flag)
367

    
368
        if self.scene() and self.scene().sceneRect().contains(self.sceneBoundingRect()):
369
            self.update()
370
            if self.scene(): self.scene().update()
371

    
372
    '''
373
        @brief      
374
        @author     humkyung
375
        @date       2018.07.27
376
    '''
377

    
378
    def mousePressEvent(self, event):
379
        if self.parentItem():
380
            if event.buttons() == Qt.LeftButton:
381
                self._savedPos = self._loc
382
                self._savedConnectedItem = self.connectedItem
383

    
384
        QGraphicsEllipseItem.mousePressEvent(self, event)
385

    
386
    '''
387
        @brief      
388
        @author     humkyung
389
        @date       2018.07.27
390
        @history    humkyung 2018.08.23 unhighlight item if underitem exists
391
    '''
392

    
393
    def mouseReleaseEvent(self, event):
394
        import shapely
395
        from SymbolSvgItem import SymbolSvgItem
396
        from EngineeringTextItem import QEngineeringTextItem
397

    
398
        try:
399
            if self.parentItem() is not None and self._savedPos is not None:
400
                items = [item for item in self.scene().items(event.scenePos()) if
401
                         item is not self and item is not self.parentItem() and
402
                         (issubclass(type(item), SymbolSvgItem) or issubclass(type(item), QEngineeringTextItem) or
403
                          type(item) is QEngineeringConnectorItem)]
404

    
405
                if items and type(items[0]) is QEngineeringConnectorItem:
406
                    self.setPos(items[0].center())
407

    
408
                    """ disconnect already connected item """
409
                    if self.connectedItem is not None:
410
                        self.connectedItem.connect(None)
411

    
412
                    """ connect each other """
413
                    self.connect(items[0])
414
                    items[0].connect(self)
415
                else:
416
                    pt = [event.scenePos().x(), event.scenePos().y()]
417

    
418
                    self.setPos(pt)
419
                    if self.connectedItem is not None:
420
                        self.connectedItem.connect(None)
421

    
422
                    self.connect(None)
423

    
424
                # unhighlight underitem
425
                if hasattr(self, '_underItem') and self._underItem is not None:
426
                    self._underItem.highlight(False)
427
                # up to here
428

    
429
                for symbol in [self.parentItem() for self in items if type(self.parentItem()) is SymbolSvgItem]:
430
                    symbol.transfer.on_pos_changed.connect(self.parent.on_symbol_pos_changed)
431

    
432
            if self.parentItem() is not None:
433
                self._savedPos = None
434
                self._savedConnectedItem = None
435
                self.transfer.onPosChanged.emit(self)
436
        finally:
437
            pass
438

    
439
        QGraphicsEllipseItem.mouseReleaseEvent(self, event)
440

    
441
    '''
442
        @brief      move connector's position while mouse drag
443
        @author     humkyung
444
        @date       2018.07.27
445
        @history    humkyung 2018.08.23 hightlight underitem
446
    '''
447

    
448
    def mouseMoveEvent(self, event):
449
        import shapely
450
        from SymbolSvgItem import SymbolSvgItem
451
        from EngineeringDimensionItem import QEngineeringDimensionItem
452
        from EngineeringTextItem import QEngineeringTextItem
453

    
454
        if self.parentItem() and self._savedPos is not None:
455
            if event.buttons() == Qt.LeftButton:
456
                items = [item for item in self.scene().items(event.scenePos()) if
457
                         item is not self and item is not self.parentItem() and \
458
                         (issubclass(type(item), SymbolSvgItem) or issubclass(type(item), QEngineeringTextItem) or
459
                          type(item) is QEngineeringConnectorItem)]
460
                # highlight underitem
461
                if len(items) > 0:
462
                    if not hasattr(self, '_underItem') or self._underItem is not items[0]:
463
                        if hasattr(self, '_underItem') and self._underItem is not None:
464
                            self._underItem.highlight(False)
465

    
466
                        self._underItem = items[0]
467
                        self._underItem.highlight(True)
468
                elif hasattr(self, '_underItem') and self._underItem is not None:
469
                    self._underItem.hoverLeaveEvent(event)
470
                    self._underItem = None
471
                # up to here
472

    
473
                if items and type(items[0]) is QEngineeringConnectorItem:
474
                    self.setPos(items[0].center())
475
                else:
476
                    self.setPos([event.scenePos().x(), event.scenePos().y()])
477

    
478
                self.update()
479

    
480
        QGraphicsEllipseItem.mouseMoveEvent(self, event)
481

    
482
    def hasConnectedItem(self):
483
        if self.connectedItem is not None:
484
            return True
485

    
486
        return False
487

    
488
    '''
489
        @brief      reject if user press Escape key
490
        @author     humkyung
491
        @date       2018.07.27
492
    '''
493

    
494
    def keyPressEvent(self, event):
495
        if event.key() == Qt.Key_Escape:
496
            if hasattr(self, '_savedPos') and self._savedPos is not None:
497
                self.setPos(self._savedPos)
498
                self.connectedItem = self._savedConnectedItem
499
                self._savedPos = None
500
                self.update()
501
        elif event.key() == Qt.Key_A:
502
            self._drawing_mode = QEngineeringConnectorItem.AXIS_MODE
503
        elif event.key() == Qt.Key_F:
504
            self._drawing_mode = QEngineeringConnectorItem.FREE_MODE
505

    
506
        QGraphicsEllipseItem.keyPressEvent(self, event)
507

    
508
    def paint(self, painter, options=None, widget=None):
509
        """override paint"""
510
        from SymbolSvgItem import SymbolSvgItem
511

    
512
        QGraphicsEllipseItem.paint(self, painter, options, widget)
513
        if type(self.parentItem()) is SymbolSvgItem:
514
            painter.setFont(self._font)
515
            painter.drawText(self.boundingRect(), str(self._conn_index), QTextOption(Qt.AlignCenter))
516

    
517
    '''
518
        @brief      check Overlap
519
        @author     kyouho
520
        @date       2018.08.30
521
    '''
522

    
523
    def isOverlapConnector(self, conn, toler=10):
524
        from shapely.geometry import Point
525

    
526
        x1 = self.center()[0]
527
        y1 = self.center()[1]
528
        x2 = conn.center()[0]
529
        y2 = conn.center()[1]
530

    
531
        if Point(x1, y1).distance(Point(x2, y2)) < toler:
532
            return True
533
        else:
534
            return False
535

    
536
    def parse_xml(self, node):
537
        """ parse given node """
538
        import uuid
539

    
540
        try:
541
            if 'UID' in node.attrib: self.uid = uuid.UUID(node.attrib['UID'], version=4)
542

    
543
            if 'CONNECTED_AT' in node.attrib:
544
                self._connected_at = QEngineeringAbstractItem.CONNECTED_AT_PT if node.attrib[
545
                                                                                     'CONNECTED_AT'] == '0' else QEngineeringAbstractItem.CONNECTED_AT_BODY
546
            else:
547
                self._connected_at = QEngineeringAbstractItem.CONNECTED_AT_PT
548

    
549
            connectedItemStr = node.find('CONNECTEDITEM').text
550
            connectPointStr = node.find('CONNECTPOINT').text.split(',')
551
            sceneConnectPointStr = node.find('SCENECONNECTPOINT').text.split(',')
552

    
553
            self._connectedItem = uuid.UUID(connectedItemStr, version=4) if connectedItemStr != 'None' else None
554
            self.connectPoint = (float(connectPointStr[0]), float(connectPointStr[1]))
555
            self.sceneConnectPoint = (float(sceneConnectPointStr[0]), float(sceneConnectPointStr[1]))
556

    
557
            self.setBrush(Qt.yellow) if self._connectedItem else self.setBrush(Qt.blue)
558
        except Exception as ex:
559
            from App import App
560
            from AppDocData import MessageType
561

    
562
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
563
                                                           sys.exc_info()[-1].tb_lineno)
564
            App.mainWnd().addMessage.emit(MessageType.Error, message)
565

    
566
    def toSql(self, index):
567
        """ generate sql string to save connectors to database """
568
        res = []
569

    
570
        try:
571
            cols = ['UID', 'Components_UID', '[Index]', 'X', 'Y']
572
            values = ['?', '?', '?', '?', '?']
573

    
574
            param = [str(self.uid), str(self.parent.uid), index, self.center()[0], self.center()[1]]
575
            sql = 'insert or replace into Points({}) values({})'.format(','.join(cols), ','.join(values))
576
            res.append((sql, tuple(param)))
577

    
578
            cols = ['Points_UID', 'Pressure', 'Pressure_Drop', 'Elevation', 'Over_Design_CV', 'CV_Type']
579
            values = ['?', '?', '?', '?', '?', '?']
580
            if self.data:
581

    
582
                pressure = self.data.pressure
583
                if pressure is None:
584
                    pressure = None
585
                else:
586
                    pressure = float(pressure)
587

    
588
                pressure_drop = self.data.pressure_drop
589
                if pressure_drop is None:
590
                    pressure_drop = None
591
                else:
592
                    pressure_drop = float(pressure_drop)
593

    
594
                elevation = self.data.elevation
595
                if elevation is None:
596
                    elevation = None
597
                else:
598
                    elevation = float(elevation)
599

    
600
                over_design_cv = self.data.over_design_cv
601
                if over_design_cv is None:
602
                    over_design_cv = None
603
                else:
604
                    over_design_cv = float(over_design_cv)
605

    
606
                cv_type = self.data.cv_type
607
                if cv_type is None:
608
                    cv_type = None
609
                else:
610
                    cv_type = str(cv_type)
611

    
612
                param = [str(self.uid), pressure, pressure_drop, elevation, over_design_cv, cv_type]
613
                sql = 'insert or replace into Nozzles({}) values({})'.format(','.join(cols), ','.join(values))
614
                res.append((sql, tuple(param)))
615

    
616
        except Exception as ex:
617
            from App import App
618
            from AppDocData import MessageType
619

    
620
            message = 'error occurred({}) in {}:{}'.format(repr(ex), sys.exc_info()[-1].tb_frame.f_code.co_filename,
621
                                                           sys.exc_info()[-1].tb_lineno)
622
            App.mainWnd().addMessage.emit(MessageType.Error, message)
623

    
624
        return res
625

    
626

    
627
'''
628
    @brief      The class transfer pyqtSignal Event. Cause Subclass of QGraphicsRectItem can't use pyqtSignal
629
    @author     Jeongwoo
630
    @date       2018.06.18
631
'''
632

    
633

    
634
class Transfer(QObject):
635
    onPosChanged = pyqtSignal(QGraphicsItem)
636
    onRemoved = pyqtSignal(QGraphicsItem)
637

    
638
    def __init__(self, parent=None):
639
        QObject.__init__(self, parent)
클립보드 이미지 추가 (최대 크기: 500 MB)