프로젝트

일반

사용자정보

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

hytos / DTI_PID / DTI_PID / LineNoTracer.py @ e215fb87

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

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

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

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

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

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

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

    
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
        return connected_items
90

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

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

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

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

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

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

    
151
        try:
152
            docData = AppDocData.instance()
153

    
154
            configs = docData.getConfigs('Line No', 'Delimiter')
155
            if 1 == len(configs):
156
                configs = docData.getConfigs('Line No', 'Configuration')
157

    
158
                for lineno in self._lineNos:
159
                    _from = lineno.prop('From')
160
                    _to = lineno.prop('To')
161
                    if _from and _to:
162
                        _from.owner = lineno
163
                        _to.owner = lineno
164
                        continue
165

    
166
                    lineno.conns.clear()
167
                    minDist = None
168
                    startLine = None
169
                    for line in self._lines:
170
                        dist = line.distanceTo((lineno.center().x(), lineno.center().y()))
171
                        if (minDist is None) or (dist < minDist):
172
                            minDist = dist
173
                            startLine = line
174
                    if (startLine is not None) and (minDist < toler):
175
                        lineno.conns.append(startLine)
176
                        startLine.owner = lineno
177
                
178
                """
179
                ## lineIndicator
180
                extendPixel = 30
181
                for lineIndicator in self._lineIndicator:
182
                    maxOverLap = 0
183
                    matchLineNo = None
184
                    liInRect = [round(lineIndicator.boundingRect().x()), round(lineIndicator.boundingRect().y()), round(lineIndicator.boundingRect().x() + lineIndicator.boundingRect().width()), round(lineIndicator.boundingRect().y() + lineIndicator.boundingRect().height())]
185
                    for remainLineNo in remainLineNos:
186
                        xOverlap = False
187
                        yOverlap = False
188
                        extendDx = remainLineNo.size[0] if lineIndicator.isVH == 'V' else remainLineNo.size[1]
189
                        reNoRect = [round(remainLineNo.loc[0] - extendDx), round(remainLineNo.loc[1] - extendDx), round(remainLineNo.loc[0] + remainLineNo.size[0] + extendDx), round(remainLineNo.loc[1] + remainLineNo.size[1] + extendDx)]
190
                        w, h = 0, 0                        
191
                        for x1 in range(reNoRect[0], reNoRect[2] + 1):
192
                            for x2 in range(liInRect[0], liInRect[2] + 1):
193
                                if x1 - x2 is 0:
194
                                    xOverlap = True
195
                                    w += 1
196
                        if not xOverlap:
197
                            continue
198
                        for y1 in range(reNoRect[1], reNoRect[3] + 1):
199
                            for y2 in range(liInRect[1], liInRect[3] + 1):
200
                                if y1 - y2 is 0:
201
                                    yOverlap = True
202
                                    h += 1
203

204
                        if not xOverlap or not yOverlap:
205
                            continue
206
                        overLapRate = (w / lineIndicator.boundingRect().width() * 100) if lineIndicator.boundingRect().width() > lineIndicator.boundingRect().height() else (h / lineIndicator.boundingRect().height() * 100)
207
                        #print(overLapRate)
208
                        if maxOverLap < overLapRate:
209
                            maxOverLap = overLapRate
210
                            matchLineNo = remainLineNo
211
                            #lineIndicator.setBrush(QBrush(QColor(255, 0, 0, 127)))
212

213
                    if matchLineNo is not None:
214
                        #print(matchLineNo.text() + ' indicator : ' + str(lineIndicator.boundingRect().x()) + ', ' + str(lineIndicator.boundingRect().y()))
215
                        matchLine = None
216
                        x1, y1, x2, y2 = int(lineIndicator.boundingRect().x() + lineIndicator.otherLine[0]), int(lineIndicator.boundingRect().y() + lineIndicator.otherLine[1]), int(lineIndicator.boundingRect().x() + lineIndicator.otherLine[2]), int(lineIndicator.boundingRect().y() + lineIndicator.otherLine[3])
217
                        #print([type(x1), type(y1), type(x2), type(y2)])
218
                        startXorY = (min(liInRect[0], liInRect[2]) - extendPixel) if lineIndicator.isVH == 'V' else (min(liInRect[1], liInRect[3]) - extendPixel)
219
                        endXorY = (max(liInRect[0], liInRect[2]) + extendPixel) if lineIndicator.isVH == 'V' else (max(liInRect[1], liInRect[3]) + extendPixel)
220
                        fXorY = []
221
                        for dXOrdY in range(startXorY, endXorY + 1): # if horizontal -> Y, if vertical -> X
222
                            if lineIndicator.isVH == 'V':
223
                                fXorY.append(round(((y2-y1)/(x2-x1))*dXOrdY + y1 - ((y2-y1)/(x2-x1))*x1)) # f(x)
224
                            else:
225
                                fXorY.append(round(((x2-x1)/(y2-y1))*dXOrdY + x1 - ((x2-x1)/(y2-y1))*y1)) # f(y)
226

227
                        #print(self._lines)
228
                        for line in self._lines:
229
                            axis1Overlap = False
230
                            axis2Overlap = False
231
                            lX1, lY1, lX2, lY2 = int(line.startPoint()[0]), int(line.startPoint()[1]), int(line.endPoint()[0]), int(line.endPoint()[1])
232
                            #print([lX1, lY1, lX2, lY2])
233

234
                            if lineIndicator.isVH == 'V':
235
                                range11 = range(startXorY, endXorY + 1)
236
                                range12 = range(min(lX1, lX2), max(lX1, lX2)+ 1)
237
                                range21 = fXorY
238
                                range22 = range(min(lY1, lY2), max(lY1, lY2) + 1)
239
                            else:
240
                                range11 = range(startXorY, endXorY + 1)
241
                                range12 = range(min(lY1, lY2), max(lY1, lY2) + 1)
242
                                range21 = fXorY
243
                                range22 = range(min(lX1, lX2), max(lX1, lX2) + 1)
244
                            #print(fXorY)
245
                            #print(type(fXorY[0]))
246
                            #print([range11[0],range11[-1], range12[0],range12[-1], range21[0],range21[-1], range22[0],range22[-1]])
247

248
                            for axis11 in range11:
249
                                for axis12 in range12:
250
                                    if axis11 - axis12 is 0:
251
                                        axis1Overlap = True
252
                                        break
253
                                if axis1Overlap: break
254
                            if not axis1Overlap:
255
                                continue
256
                            for axis21 in range21:
257
                                for axis22 in range22:
258
                                    if axis21 - axis22 is 0:
259
                                        axis2Overlap = True
260
                                        break
261
                                if axis2Overlap: break
262
                            
263
                            if axis1Overlap and axis2Overlap:
264
                                matchLine = line
265
                                break
266
                        if matchLine is not None:
267
                            matchLineNo.conns.append(matchLine)
268
                            lineIndicator.lineIndicator = 'Match'
269
                            lineIndicator.setBrush(QBrush(QColor(0, 0, 255, 127)))
270
                            #print('connected ' + matchLineNo.text() + ' and ' + matchLine.uid)                     
271
                ## up to here
272
                """
273
                            
274
                maxValue = len(self._lineNos) + 1   ## line no's count + secondary line
275

    
276
                # find primary lines
277
                # sort line no with from,to value
278
                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)
279
                for lineno in self._lineNos:
280
                    if displayMessage: displayMessage.emit('{} {}'.format(lineno.text(), 'Topology Construction'))
281
                    self.find_primary_lines(lineno)
282
                    if updateProgress: updateProgress.emit(maxValue)
283

    
284
                # find secondary lines
285
                lines = self._lines
286
                self.find_secondary_lines(lines)
287
                if updateProgress: updateProgress.emit(maxValue)
288

    
289
            ### make trim lines
290
            """
291
            updateProgress.emit(-1) # reset progressbar
292
            displayMessage.emit('Unknown line Topology Construction')
293
            orphanLines = [line for line in self._lines if line.owner is None] 
294
            if orphanLines:
295
                maxValue = len(orphanLines)
296
                orphanLines = sorted(orphanLines, key=lambda param:param.length(), reverse=True)
297
                while len(orphanLines) > 0:
298
                    trimLineNo = QEngineeringTrimLineNoTextItem()
299
                    trimLineNo.conns.append(orphanLines[0])
300
                    orphanLines[0].owner = trimLineNo
301
                    orphanLines[0].linkedItem = trimLineNo
302

303
                    connectedItems = self.findPrimaryLines(trimLineNo)
304
                    for item in connectedItems:
305
                        if item in orphanLines:
306
                            orphanLines.remove(item)
307
                            updateProgress.emit(maxValue)
308

309
                    self.findSecondaryLines(orphanLines)
310
                    for item in orphanLines[:]:
311
                        if item.owner is not None:
312
                            orphanLines.remove(item)
313
                            updateProgress.emit(maxValue)
314

315
                    docData.tracerLineNos.append(trimLineNo)
316
            """
317
            if updateProgress: updateProgress.emit(maxValue)
318
        except Exception as ex:
319
            from App import App 
320

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

    
324
    '''
325
        @brief      find objects connected to given line while loop
326
        @author     humkyung
327
        @date       2018.04.16
328
        @history    humkyung 2018.05.08 find symbol or line connected to given object
329
                    humkyung 2018.05.10 set found object's owner
330
                    humkyung 2018.05.17 try to connect both symbol and line
331
                    humkyung 2018.06.22 order connected objects
332
    '''
333
    def find_connected_objects(self, start, to=None):
334
        from EngineeringLineItem import QEngineeringLineItem
335
        from EngineeringEquipmentItem import QEngineeringEquipmentItem
336
        from SymbolSvgItem import SymbolSvgItem
337

    
338
        visited = [start]
339

    
340
        try:
341
            pool = []
342
            pool.append((0, start))
343
            
344
            while len(pool) > 0:
345
                sign, obj = pool.pop()
346

    
347
                """ check obj is upstream or downstream of spec break """
348
                matches = [spec_break for spec_break in self._spec_breaks if spec_break.is_connected(obj)]
349
                if matches or issubclass(type(obj), QEngineeringEquipmentItem): continue
350
                """ end loop if obj is to """
351
                if to is not None and str(obj.uid) == str(to.uid): break
352

    
353
                if type(obj) is QEngineeringLineItem:
354
                    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)]
355
                    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)]
356

    
357
                elif issubclass(type(obj), SymbolSvgItem):
358
                    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)]
359

    
360
                    if len(lineMatches) > 1: # choose one if connected lines are more than 2
361
                        matches = [x for x in [visited[0], visited[-1]] if obj.is_connected(x)]
362
                        if matches:
363
                            next_connected = [x for x in lineMatches if obj.next_connected(x, matches[0])]
364
                            if next_connected: lineMatches = next_connected
365

    
366
                    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)]
367

    
368
                # order connected objects
369
                matches = []
370
                matches.extend(symbolMatches)
371
                matches.extend(lineMatches)
372

    
373
                if sign == 0 and len(matches) > 1:
374
                    mid = int(len(matches)*0.5)
375
                    lhs = matches[0:mid]
376
                    rhs = matches[mid:]
377
                elif sign == -1:
378
                    lhs = matches
379
                    rhs = []
380
                else:
381
                    lhs = []
382
                    rhs = matches
383

    
384
                for match in lhs:
385
                    pool.append((-1, match))
386
                    visited.insert(0, match)
387

    
388
                for match in rhs:
389
                    pool.append((1, match))
390
                    visited.append(match)
391
                # up to here
392

    
393
        except Exception as ex:
394
            from App import App 
395

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

    
399
        return visited
400

    
401
'''
402
    @brief      connect attributes
403
    @author     humkyung
404
    @date       2018.06.17
405
    @history    humkyung 2018.06.21 paste connect attributes codes from recognizeLine function
406
                kyouho  2018.09.14  clear Item's owner 
407
'''
408
def connectAttrImpl(worker, update_line_type):
409
    from LineNoTracer import LineNoTracer
410
    from AppDocData import AppDocData
411
    from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
412
    from EngineeringInstrumentItem import QEngineeringInstrumentItem
413
    from EngineeringReducerItem import QEngineeringReducerItem
414
    from EngineeringEquipmentItem import QEngineeringEquipmentItem
415
    from QEngineeringOPCItem import QEngineeringOPCItem
416
    from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
417
    from EngineeringVendorItem import QEngineeringVendorItem
418
    from EngineeringEndBreakItem import QEngineeringEndBreakItem
419

    
420
    try:
421
        symbols = []
422
        lines = []
423
        lineNos = []
424
        specBreak = []
425
        lineIndicator = []
426
        vendor_packages = [item for item in worker.graphicsView.scene.items() if type(item) is QEngineeringVendorItem]
427
        end_breaks = [item for item in worker.graphicsView.scene.items() if type(item) is QEngineeringEndBreakItem]
428
        for end_break in end_breaks:
429
            worker.graphicsView.scene.removeItem(end_break)
430

    
431
        configs = AppDocData.instance().getConfigs('Supplied by Tag Rule', 'by Vendor')
432
        vendorTag = configs[0].value if configs else 'By Vendor'
433
        for item in worker.graphicsView.scene.items():
434
            if type(item) is QEngineeringSpecBreakItem:
435
                specBreak.append(item)
436
            elif issubclass(type(item), SymbolSvgItem) and not type(item) is QEngineeringUnknownItem:
437
                matches = [vendor_package for vendor_package in vendor_packages if vendor_package.includes(item)]
438
                if matches:
439
                    item.set_property('Supplied By', vendorTag)
440
                else:
441
                    item.set_property('Supplied By', '')
442
                    symbols.append(item)
443
            elif type(item) is QEngineeringLineNoTextItem:
444
                lineNos.append(item)
445
            elif type(item) is QEngineeringLineItem:
446
                matches = [vendor_package for vendor_package in vendor_packages if vendor_package.includes(item)]
447
                if not matches: lines.append(item)
448
            elif type(item) is QEngineeringUnknownItem and item.lineIndicator != 'False':
449
                lineIndicator.append(item)
450

    
451
        # trace line no
452
        tracer = LineNoTracer(symbols, lines, lineNos, specBreak, lineIndicator, vendor_packages)
453
        tracer.execute(worker.displayMessage, worker.updateProgress)
454
        # up to here
455

    
456
        # connect attribute
457
        texts = [item for item in worker.graphicsView.scene.items() if issubclass(type(item), QEngineeringTextItem)]
458
        for symbol in symbols:
459
            try:
460
                symbol.connectAttribute(texts)
461
            except Exception as ex:
462
                from App import App 
463
                message = 'error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
464
                App.mainWnd().addMessage.emit(MessageType.Error, message)
465

    
466
        """ try to connect label to valve """
467
        labels = [symbol for symbol in symbols if type(symbol) is QEngineeringInstrumentItem and not symbol.is_connected]
468
        valves = [symbol for symbol in symbols if type(symbol) is not QEngineeringInstrumentItem and type(symbol) is not QEngineeringReducerItem and type(symbol) is not QEngineeringEquipmentItem \
469
            and type(symbol) is not QEngineeringOPCItem and type(symbol) is not QEngineeringSpecBreakItem]
470
        for valve in valves:
471
            valve.connectAttribute(labels, clear=False)
472

    
473
        """ update line type """
474
        if update_line_type == True:
475
            lines = [line for line in worker.graphicsView.scene.items() if type(line) is QEngineeringLineItem]
476
            #for line in lines: line.lineType = 'Primary'
477
            for line in lines: line.update_line_type()
478

    
479
        """make end break"""
480
        end_breaks = []
481
        for lineNo in lineNos:
482
            for end_break in lineNo.end_break():
483
                end_breaks.append(end_break)
484
        
485
            for end_break in end_breaks:
486
                end_break.addSvgItemToScene(worker.graphicsView.scene)
487

    
488
        if end_breaks:
489
            # check dulplication
490
            dupl = set()
491
            for i in range(len(end_breaks)):
492
                for j in range(len(end_breaks)):
493
                    if i == j:
494
                        continue
495
                    else:
496
                        setI = set([end_breaks[i].owner, end_breaks[i].prop('Connected Item')])
497
                        setJ = set([end_breaks[j].owner, end_breaks[j].prop('Connected Item')])
498
                        if not (setI - setJ):
499
                            index = [i, j]
500
                            index.sort()
501
                            index = tuple(index)
502
                            dupl.add(index)
503

    
504
            dupl = [indexSet[1] for indexSet in list(dupl)]
505
            dupl.sort(reverse=True)
506
            for index in dupl:
507
                end_breaks.pop(index)
508

    
509
    except Exception as ex:
510
        from App import App 
511
        message = 'error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
512
        App.mainWnd().addMessage.emit(MessageType.Error, message)
513
    finally:
514
        worker.finished.emit()
클립보드 이미지 추가 (최대 크기: 500 MB)