프로젝트

일반

사용자정보

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

hytos / DTI_PID / DTI_PID / LineNoTracer.py @ c3a5995f

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

1
# coding: utf-8
2
""" This is line no tracer module """
3

    
4
import sys
5
import math
6
import shapely
7
from AppDocData import AppDocData, MessageType
8
from EngineeringLineItem import QEngineeringLineItem
9
from EngineeringLineNoTextItem import QEngineeringLineNoTextItem
10
from QEngineeringFlowArrowItem import QEngineeringFlowArrowItem
11
from SymbolSvgItem import SymbolSvgItem
12
from EngineeringTextItem import QEngineeringTextItem
13
from EngineeringUnknownItem import QEngineeringUnknownItem
14
try:
15
    from PyQt5.QtCore import *
16
    from PyQt5.QtGui import *
17
    from PyQt5.QtWidgets import *
18
except ImportError:
19
    try:
20
        from PyQt4.QtCore import *
21
        from PyQt4.QtGui import *
22
    except ImportError:
23
        raise ImportError("ImageViewerQt: Requires PyQt5 or PyQt4.")
24

    
25
class LineNoTracer:
26
    '''
27
        @history    2018.04.26 Jeongwoo Variable name changed (texts → lineNos)
28
    '''
29
    def __init__(self, symbols, lines, lineNos, specBreaks, lineIndicators, vendors, end_breaks):
30
        try:
31
            self._symbols = symbols
32
            self._lines = lines
33
            self._lineNos = lineNos
34
            self._spec_breaks = specBreaks
35
            self._lineIndicator = lineIndicators
36
            self._end_breaks = end_breaks
37
            
38
            """
39
            for spec in self._specBreak:
40
                for attr in spec.attrs:
41
                    if type(attr) is tuple and attr[1] != '':
42
                        self._specBreakUID.append(attr[1])
43
            """
44
        except Exception as ex:
45
            from App import App 
46

    
47
            message = 'error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
48
            App.mainWnd().addMessage.emit(MessageType.Error, message)
49

    
50
    '''
51
        @brief  find primary lines connected to given line no
52
        @author humkyung
53
    '''
54
    def find_primary_lines(self, lineno):
55
        from EngineeringLineItem import QEngineeringLineItem
56
        from EngineeringRunItem import QEngineeringRunItem
57

    
58
        connected_items = []
59
        
60
        _from = lineno.prop('From')
61
        _to = lineno.prop('To')
62
        if _from and _to and lineno.empty():
63
            connected_items = self.find_connected_objects(_from, to=_to)
64
            if _from in connected_items and _to in connected_items:
65
                start = connected_items.index(_from)
66
                end = connected_items.index(_to)
67
                if start < end:
68
                    connected_items = connected_items[start:end+1]
69
                else:
70
                    connected_items = connected_items[end:start+1]
71
                    connected_items.reverse()
72
        elif (not _from or not _to) and (1 == len(lineno.conns)):
73
            connected_items = self.find_connected_objects(lineno.conns[0])
74

    
75
        #print(connected_items)
76
        if connected_items:
77
            for item in connected_items: 
78
                item.owner = lineno # set item's owner
79
            
80
            line_run = QEngineeringRunItem()
81
            line_run.items = connected_items
82
            line_run.arrange_flow_direction()
83
            line_run.owner = lineno
84
            lineno.runs.append(line_run)
85

    
86
            lineno.set_property('From', connected_items[0])
87
            lineno.set_property('To', connected_items[-1])
88

    
89
        if _to is not None and connected_items and connected_items[-1] is not _to:
90
            _to.owner = None
91

    
92
        return connected_items
93

    
94
    '''
95
        @brief  find secondary lines
96
        @author humkyung
97
    '''
98
    def find_secondary_lines(self, lines):
99
        from EngineeringAbstractItem import QEngineeringAbstractItem
100
        from EngineeringLineItem import QEngineeringLineItem
101
        from EngineeringRunItem import QEngineeringRunItem
102

    
103
        try:
104
            foundCount = 1
105
            while foundCount:
106
                foundCount = 0
107
                notMatches = []
108
                for line in lines:
109
                    if line.owner is not None: continue
110

    
111
                    line_matches = [x for x in self._lines if x.owner and line.is_connected(x, QEngineeringAbstractItem.CONNECTED_AT_BODY)]
112
                    symbol_matches = [x for x in self._symbols if x.owner and line.is_connected(x) and x.canBeSecondary(line)]
113
                    if line_matches or symbol_matches:
114
                        foundCount += 1
115
                        connected_items = self.find_connected_objects(line)
116
                        
117
                        owner = line_matches[0].owner if line_matches else symbol_matches[0].owner
118
                        for item in connected_items:
119
                            item.owner = owner # set item's owner
120

    
121
                        line_run = QEngineeringRunItem()
122
                        line_run.items = connected_items
123
                        line_run.arrange_flow_direction()
124
                        if line_run.items is not None and len(line_run.items) > 0:
125
                            line_run.owner = owner
126
                            owner.runs.append(line_run)
127
                    else:
128
                        notMatches.append(line)
129
                lines = notMatches
130
        except Exception as ex:
131
            from App import App 
132

    
133
            message = 'error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
134
            App.mainWnd().addMessage.emit(MessageType.Error, message)
135

    
136
    '''
137
        @brief      trace line no
138
        @author     humkyung
139
        @date       2018.04.16
140
        @history    2018.04.26 Jeongwoo docDatalineNos = self.lineNos, Not For loop
141
                    humkyung 2018.05.08 add flow arrow
142
                    Jeongwoo 2018.05.14 Add [runs] on Primary/Secondary Line - Need to modify Secondary Line
143
                    Jeongwoo 2018.05.15 Make Comments [lineno.conns[0].owner = lineno]
144
                    Jeongwoo 2018.05.17 Modify find secondary lines with 'while'
145
                                        Modify find secondary lines with 'while' used sublist for unconnected line
146
                    humkyung 2018.05.18 set start line's owner before tracing
147
    '''
148
    def execute(self, displayMessage, updateProgress, toler=300):
149
        from EngineeringLineItem import QEngineeringLineItem
150
        from SymbolSvgItem import SymbolSvgItem
151
        from EngineeringRunItem import QEngineeringRunItem
152
        from QEngineeringTrimLineNoTextItem import QEngineeringTrimLineNoTextItem
153

    
154
        try:
155
            docData = AppDocData.instance()
156

    
157
            configs = docData.getConfigs('Line No', 'Delimiter')
158
            if 1 == len(configs):
159
                configs = docData.getConfigs('Line No', 'Configuration')
160

    
161
                for lineno in self._lineNos:
162
                    _from = lineno.prop('From')
163
                    _to = lineno.prop('To')
164

    
165
                    lineno.conns.clear()
166
                    minDist = None
167
                    startLine = None
168
                    for line in self._lines:
169
                        dist = line.distanceTo((lineno.center().x(), lineno.center().y()))
170
                        if (minDist is None) or (dist < minDist):
171
                            minDist = dist
172
                            startLine = line
173
                    if (startLine is not None) and (minDist < toler):
174
                        lineno.conns.append(startLine)
175
                        startLine.owner = lineno
176

    
177
                    if _from and _to and (type(_from) is QEngineeringLineItem or issubclass(type(_from), SymbolSvgItem)) and (type(_to) is QEngineeringLineItem or issubclass(type(_to), SymbolSvgItem)):
178
                        _from.owner = lineno
179
                        _to.owner = lineno
180
                        continue
181
                    else:
182
                        lineno.set_property('From', None)
183
                        lineno.set_property('To', None)
184

    
185
                maxValue = len(self._lineNos) + 1   ## line no's count + secondary line
186

    
187
                # find primary lines
188
                # sort line no with from,to value
189
                self._lineNos.sort(key=lambda line_no:(1 if line_no.prop('From') else 0)+(1 if line_no.prop('To') else 0),reverse=True)
190
                for lineno in self._lineNos:
191
                    if displayMessage: displayMessage.emit('{} {}'.format(lineno.text(), 'Topology Construction'))
192
                    self.find_primary_lines(lineno)
193
                    if updateProgress: updateProgress.emit(maxValue)
194

    
195
                # find secondary lines
196
                lines = self._lines
197
                self.find_secondary_lines(lines)
198
                if updateProgress: updateProgress.emit(maxValue)
199

    
200
            ### make trim lines
201
            updateProgress.emit(-1) # reset progressbar
202
            displayMessage.emit('Unknown line Topology Construction')
203
            orphanLines = [line for line in self._lines if line.owner is None] 
204
            if orphanLines:
205
                maxValue = len(orphanLines)
206
                orphanLines = sorted(orphanLines, key=lambda param:param.length(), reverse=True)
207
                while len(orphanLines) > 0:
208
                    trimLineNo = QEngineeringTrimLineNoTextItem()
209
                    trimLineNo.conns.append(orphanLines[0])
210
                    orphanLines[0].owner = trimLineNo
211
                    #orphanLines[0].linkedItem = trimLineNo
212

    
213
                    connectedItems = self.find_primary_lines(trimLineNo)
214
                    for item in connectedItems:
215
                        if item in orphanLines:
216
                            orphanLines.remove(item)
217
                            updateProgress.emit(maxValue)
218

    
219
                    self.find_secondary_lines(orphanLines)
220
                    for item in orphanLines[:]:
221
                        if item.owner is not None:
222
                            orphanLines.remove(item)
223
                            updateProgress.emit(maxValue)
224

    
225
                    docData.tracerLineNos.append(trimLineNo)
226

    
227
            #(docData.tracerLineNos))
228
            if updateProgress: updateProgress.emit(maxValue)
229
        except Exception as ex:
230
            from App import App 
231

    
232
            message = 'error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
233
            App.mainWnd().addMessage.emit(MessageType.Error, message)
234

    
235
    '''
236
        @brief      find objects connected to given line while loop
237
        @author     humkyung
238
        @date       2018.04.16
239
        @history    humkyung 2018.05.08 find symbol or line connected to given object
240
                    humkyung 2018.05.10 set found object's owner
241
                    humkyung 2018.05.17 try to connect both symbol and line
242
                    humkyung 2018.06.22 order connected objects
243
    '''
244
    def find_connected_objects(self, start, to=None):
245
        from EngineeringLineItem import QEngineeringLineItem
246
        from EngineeringEquipmentItem import QEngineeringEquipmentItem
247
        from SymbolSvgItem import SymbolSvgItem
248

    
249
        visited = [start]
250

    
251
        try:
252
            pool = []
253
            pool.append((0, start))
254
            
255
            while len(pool) > 0:
256
                sign, obj = pool.pop()
257
                
258
                #""" check obj is upstream or downstream of spec break """
259
                #matches = [spec_break for spec_break in self._spec_breaks if spec_break.is_connected(obj)]
260
                #if matches or issubclass(type(obj), QEngineeringEquipmentItem):
261
                #    visited.pop(visited.index(obj))
262
                #    continue
263
                #print('obj={} pool={}'.format(obj, pool))
264

    
265
                match = False
266
                for end_break in self._end_breaks: 
267
                    if obj is end_break.owner or obj is end_break.prop('Connected Item'):
268
                        match = True
269
                        break
270

    
271
                if issubclass(type(obj), QEngineeringEquipmentItem):
272
                    visited.pop(visited.index(obj))
273
                    continue    
274
                elif match:
275
                    continue
276

    
277
                """ end loop if obj is to """
278
                if to is not None and str(obj.uid) == str(to.uid): break
279

    
280
                # nextmatches list always has one item
281
                if type(obj) is QEngineeringLineItem:
282
                    symbolMatches = [x for x in self._symbols if (x.owner is None or x.owner == start.owner) and (x not in visited) and obj.is_connected(x)]
283
                    lineMatches = [x for x in self._lines if (x.owner is None or x.owner == start.owner) and (x is not obj) and (x not in visited) and obj.is_connected(x)]
284
                    nextMatches = symbolMatches + lineMatches
285

    
286
                elif issubclass(type(obj), SymbolSvgItem):
287
                    # symbol can be connected with line and another symbol at the same time
288
                    lineMatches = [x for x in self._lines if (x.owner is None or x.owner == start.owner) and (x not in visited) and obj.is_connected(x)]
289
                    symbolMatches = [x for x in self._symbols if (x.owner is None or x.owner == start.owner) and (x is not obj) and (x not in visited) and obj.is_connected(x, None)]
290
                    nextMatches = symbolMatches + lineMatches
291

    
292
                    if len(nextMatches) > 1: # choose one if connected items are more than 2
293
                        matches = [x for x in visited if obj.is_connected(x)]
294
                        if matches:
295
                            next_connected = [x for x in nextMatches if obj.next_connected(x, matches[0])]
296

    
297
                            if next_connected:
298
                                nextMatches = next_connected
299
                            else:
300
                                nextMatches = [lineMatches[0]]
301
                
302
                # order connected objects
303
                matches = []
304
                matches.extend(nextMatches)
305

    
306
                if sign == 0 and len(matches) > 1:
307
                    mid = int(len(matches)*0.5)
308
                    lhs = matches[0:mid]
309
                    rhs = matches[mid:]
310
                elif sign == -1:
311
                    lhs = matches
312
                    rhs = []
313
                else:
314
                    lhs = []
315
                    rhs = matches
316

    
317
                for match in lhs:
318
                    #print(match)
319
                    pool.append((-1, match))
320
                    visited.insert(0, match)
321

    
322
                for match in rhs:
323
                    #print(match)
324
                    pool.append((1, match))
325
                    visited.append(match)
326
                # up to here
327

    
328
        except Exception as ex:
329
            from App import App 
330

    
331
            message = 'error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
332
            App.mainWnd().addMessage.emit(MessageType.Error, message)
333

    
334
        #print(visited)
335
        return visited
336

    
337
'''
338
    @brief      connect attributes
339
    @author     humkyung
340
    @date       2018.06.17
341
    @history    humkyung 2018.06.21 paste connect attributes codes from recognizeLine function
342
                kyouho  2018.09.14  clear Item's owner 
343
'''
344
def connectAttrImpl(worker, update_line_type):
345
    import os
346
    from App import App
347
    from LineNoTracer import LineNoTracer
348
    from AppDocData import AppDocData
349
    from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
350
    from EngineeringInstrumentItem import QEngineeringInstrumentItem
351
    from EngineeringReducerItem import QEngineeringReducerItem
352
    from EngineeringEquipmentItem import QEngineeringEquipmentItem
353
    from QEngineeringOPCItem import QEngineeringOPCItem
354
    from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
355
    from EngineeringVendorItem import QEngineeringVendorItem
356
    from EngineeringEndBreakItem import QEngineeringEndBreakItem
357
    from EngineeringReservedWordTextItem import QEngineeringReservedWordTextItem
358
    from QEngineeringSizeTextItem import QEngineeringSizeTextItem
359
    from EngineeringValveOperCodeTextItem import QEngineeringValveOperCodeTextItem
360
    from QEngineeringTrimLineNoTextItem import QEngineeringTrimLineNoTextItem
361
    from QEngineeringTagNoTextItem import QEngineeringTagNoTextItem
362

    
363
    try:
364
        docdata = AppDocData.instance()
365
        symbols = []
366
        lines = []
367
        lineNos = []
368
        specBreak = []
369
        lineIndicator = []
370
        vendor_packages = [item for item in worker.graphicsView.scene.items() if type(item) is QEngineeringVendorItem]
371
        end_breaks = []
372
        for end_break in [item for item in worker.graphicsView.scene.items() if type(item) is QEngineeringEndBreakItem]:
373
            if not end_break.prop('Freeze'):
374
                end_break.transfer.onRemoved.emit(end_break)
375
                #worker.graphicsView.scene.removeItem(end_break)
376
            else:
377
                end_breaks.append(end_break)
378

    
379
        configs = docdata.getConfigs('Supplied by Tag Rule', 'by Vendor')
380
        vendorTag = configs[0].value if configs else 'By Vendor'
381
        for item in worker.graphicsView.scene.items():
382
            if type(item) is QEngineeringSpecBreakItem:
383
                specBreak.append(item)
384
            elif issubclass(type(item), SymbolSvgItem) and not type(item) is QEngineeringUnknownItem:
385
                matches = [vendor_package for vendor_package in vendor_packages if vendor_package.includes(item)]
386
                if matches:
387
                    item.set_property('Supplied By', vendorTag)
388
                else:
389
                    item.set_property('Supplied By', '')
390
                symbols.append(item)
391
            elif type(item) is QEngineeringLineNoTextItem:
392
                lineNos.append(item)
393
            elif type(item) is QEngineeringLineItem:
394
                #matches = [vendor_package for vendor_package in vendor_packages if vendor_package.includes(item)]
395
                #if not matches: lines.append(item)
396
                lines.append(item)
397
            elif type(item) is QEngineeringUnknownItem and item.lineIndicator != 'False':
398
                lineIndicator.append(item)
399
            elif issubclass(type(item), QEngineeringTextItem):
400
                item.owner = None
401

    
402
        # remove unknown line's
403
        pastTrim = docdata.tracerLineNos
404
        treeWidget = App.mainWnd().itemTreeWidget
405
        for pastTrimIndex in reversed(range(len(pastTrim))):
406
            if type(pastTrim[pastTrimIndex]) is QEngineeringTrimLineNoTextItem:
407
                try:
408
                    connected_items = pastTrim[pastTrimIndex].getConnectedItems()
409
                    for item in connected_items:
410
                        treeWidget.addTreeItem(treeWidget.SymbolsTreeItem, item)
411
                    pastTrim[pastTrimIndex].explode()
412
                finally:
413
                    pass
414

    
415
        # trace line no
416
        tracer = LineNoTracer(symbols, lines, lineNos, specBreak, lineIndicator, vendor_packages, end_breaks)
417
        tracer.execute(worker.displayMessage, worker.updateProgress)
418
        # up to here
419

    
420
        # connect attribute
421
        texts = [item for item in worker.graphicsView.scene.items() if issubclass(type(item), QEngineeringTextItem)]
422
        for text in texts:
423
            text.onwer = None
424
        for symbol in symbols:
425
            try:
426
                symbol.connectAttribute(texts)
427
            except Exception as ex:
428
                from App import App 
429
                message = 'error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
430
                App.mainWnd().addMessage.emit(MessageType.Error, message)
431

    
432
        """ try to connect label to valve """
433
        labels = [symbol for symbol in symbols if type(symbol) is QEngineeringInstrumentItem and not symbol.has_connection]
434
        #print(labels[0])
435
        valves = [symbol for symbol in symbols if (type(symbol) is QEngineeringInstrumentItem and symbol.has_connection) and type(symbol) is not QEngineeringReducerItem and type(symbol) is not QEngineeringEquipmentItem \
436
            and type(symbol) is not QEngineeringOPCItem and type(symbol) is not QEngineeringSpecBreakItem]
437
        #print(valves[0])
438
        for valve in valves:
439
            valve.connectAttribute(labels, clear=False)
440

    
441
        """ try to find text item's owner """
442
        texts = [item for item in worker.graphicsView.scene.items() if type(item) is QEngineeringReservedWordTextItem]
443
        for text in texts:
444
            text.findOwner(lines)
445
        texts = [item for item in worker.graphicsView.scene.items() if type(item) is QEngineeringSizeTextItem or type(item) is QEngineeringSizeTextItem or type(item) is QEngineeringValveOperCodeTextItem or type(item) is QEngineeringTagNoTextItem]            
446
        for text in texts:
447
            text.findOwner(symbols)
448

    
449
        """ update line type """
450
        if update_line_type == True:
451
            #lines = [line for line in worker.graphicsView.scene.items() if type(line) is QEngineeringLineItem]
452
            #for line in lines: line.lineType = 'Primary'
453
            for line in lines: line.update_line_type()
454

    
455
        """make end break"""
456
        end_break_names = docdata.getSymbolListByType('type', 'End Break')
457
        if len(end_break_names) is not 0:
458
                
459
            svgFileName = end_break_names[0].sName
460
            symbol = docdata.getSymbolByQuery('name', svgFileName)
461
            svgFilePath = os.path.join(docdata.getCurrentProject().getSvgFilePath(), symbol.getType(), svgFileName+'.svg')
462

    
463
            #end_breaks = []
464
            lineNo_froms = []
465
            lineNo_tos = []
466

    
467
            for lineNo in lineNos:
468
                lineNo_froms.append(lineNo.prop('From')) if lineNo.prop('From') is not None else None
469
                lineNo_tos.append(lineNo.prop('To')) if lineNo.prop('To') is not None else None
470
                #end_breaks.extend(lineNo.end_break())
471

    
472
            for line_end in lineNo_froms + lineNo_tos:
473
                #print(type(line_end))
474
                for connector in line_end.connectors:
475
                    if connector.connectedItem is not None and connector.connectedItem.owner is not line_end.owner:
476
                        end_break = SymbolSvgItem.createItem(symbol.getType(), svgFilePath)
477
                        pt = [connector.center()[0] - float(symbol.getOriginalPoint().split(',')[0]), connector.center()[1] - float(symbol.getOriginalPoint().split(',')[1])]
478
                        origin = [0,0]
479
                        if 2 == len(symbol.getOriginalPoint().split(',')):
480
                            tokens = symbol.getOriginalPoint().split(',')
481
                            origin = [pt[0] + float(tokens[0]), pt[1] + float(tokens[1])]
482
                        end_break.buildItem(svgFileName, symbol.getType(), 5.7, pt, [end_break.boundingRect().width(), end_break.boundingRect().height()], origin, [], symbol.getBaseSymbol(), symbol.getAdditionalSymbol(), symbol.getHasInstrumentLabel())
483
        
484
                        end_break.set_property('Connected Item', connector.connectedItem)
485
                        end_break.setToolTip('owner : ' + str(line_end))
486
                        end_break.area = 'Drawing'
487
                        end_break.owner = line_end
488
                        end_breaks.append(end_break)
489
            
490
            if end_breaks:
491
            # check dulplication
492
                dupl = set()
493
                for i in range(len(end_breaks)):
494
                    for j in range(len(end_breaks)):
495
                        if i == j: continue
496
                        else:
497
                            setI = set([end_breaks[i].owner, end_breaks[i].prop('Connected Item')])
498
                            setJ = set([end_breaks[j].owner, end_breaks[j].prop('Connected Item')])
499
                            if not (setI - setJ):
500
                                index = [i, j]
501
                                index.sort()
502
                                index = tuple(index)
503
                                dupl.add(index)
504
                #print(dupl)
505
                dupl = list(set([(indexSet[1] if not end_breaks[indexSet[1]].prop('Freeze') else indexSet[0]) for indexSet in list(dupl)]))
506
                dupl.sort(reverse=True)
507
                #print(dupl)
508
                for index in dupl:
509
                    end_breaks.pop(index)
510

    
511
                for end_break in end_breaks:
512
                    if not end_break.prop('Freeze'):
513
                        end_break.transfer.onRemoved.connect(App.mainWnd().itemRemoved)
514
                        end_break.addSvgItemToScene(worker.graphicsView.scene)
515

    
516
    except Exception as ex:
517
        from App import App 
518
        message = 'error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
519
        App.mainWnd().addMessage.emit(MessageType.Error, message)
520
    finally:
521
        worker.finished.emit()
522

    
클립보드 이미지 추가 (최대 크기: 500 MB)