프로젝트

일반

사용자정보

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

hytos / DTI_PID / DTI_PID / LineNoTracer.py @ 990644d9

이력 | 보기 | 이력해설 | 다운로드 (22.3 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
            self._vendor = vendor
39
            
40
            """
41
            for spec in self._specBreak:
42
                for attr in spec.attrs:
43
                    if type(attr) is tuple and attr[1] != '':
44
                        self._specBreakUID.append(attr[1])
45
            """
46
        except Exception as ex:
47
            from App import App 
48

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

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

    
60
        connected_items = []
61
        
62
        _from = lineno.get_property('From')
63
        _to = lineno.get_property('To')
64
        if _from and _to and lineno.empty():
65
            connected_items = self.find_connected_objects(_from, to=_to)
66
        elif (not _from or not _to) and (1 == len(lineno.conns)):
67
            connected_items = self.find_connected_objects(lineno.conns[0])
68

    
69
        if connected_items:
70
            for item in connected_items: 
71
                item.owner = lineno # set item's owner
72
            
73
            line_run = QEngineeringRunItem()
74
            line_run.items = connected_items
75
            line_run.arrange_flow_direction()
76
            line_run.owner = lineno
77
            lineno.runs.append(line_run)
78

    
79
            lineno.set_property('From', connected_items[0])
80
            lineno.set_property('To', connected_items[-1])
81

    
82
        return connected_items
83

    
84
    '''
85
        @brief  find secondary lines
86
        @author humkyung
87
    '''
88
    def find_secondary_lines(self, lines):
89
        from EngineeringAbstractItem import QEngineeringAbstractItem
90
        from EngineeringLineItem import QEngineeringLineItem
91
        from EngineeringRunItem import QEngineeringRunItem
92

    
93
        try:
94
            foundCount = 1
95
            while foundCount:
96
                foundCount = 0
97
                notMatches = []
98
                for line in lines:
99
                    if line.owner is not None: continue
100

    
101
                    line_matches = [x for x in self._lines if x.owner and line.is_connected(x, QEngineeringAbstractItem.CONNECTED_AT_BODY)]
102
                    symbol_matches = [x for x in self._symbols if x.owner and line.is_connected(x)]
103
                    if line_matches or symbol_matches:
104
                        foundCount += 1
105
                        connected_items = self.find_connected_objects(line)
106
                        
107
                        owner = line_matches[0].owner if line_matches else symbol_matches[0].owner
108
                        for item in connected_items:
109
                            item.owner = owner # set item's owner
110

    
111
                        line_run = QEngineeringRunItem()
112
                        line_run.items = connected_items
113
                        line_run.arrange_flow_direction()
114
                        if line_run.items is not None and len(line_run.items) > 0:
115
                            line_run.owner = owner
116
                            owner.runs.append(line_run)
117
                    else:
118
                        notMatches.append(line)
119
                lines = notMatches
120
        except Exception as ex:
121
            from App import App 
122

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

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

    
144
        try:
145
            docData = AppDocData.instance()
146

    
147
            configs = docData.getConfigs('Line No', 'Delimiter')
148
            if 1 == len(configs):
149
                configs = docData.getConfigs('Line No', 'Configuration')
150

    
151
                for lineno in self._lineNos:
152
                    _from = lineno.get_property('From')
153
                    _to = lineno.get_property('To')
154
                    if _from and _to: continue
155

    
156
                    lineno.conns.clear()
157
                    minDist = None
158
                    startLine = None
159
                    for line in self._lines:
160
                        dist = line.distanceTo((lineno.center().x(), lineno.center().y()))
161
                        if (minDist is None) or (dist < minDist):
162
                            minDist = dist
163
                            startLine = line
164
                    if (startLine is not None) and (minDist < toler):
165
                        lineno.conns.append(startLine)
166
                        startLine.owner = lineno
167
                
168
                """
169
                ## lineIndicator
170
                extendPixel = 30
171
                for lineIndicator in self._lineIndicator:
172
                    maxOverLap = 0
173
                    matchLineNo = None
174
                    liInRect = [round(lineIndicator.boundingRect().x()), round(lineIndicator.boundingRect().y()), round(lineIndicator.boundingRect().x() + lineIndicator.boundingRect().width()), round(lineIndicator.boundingRect().y() + lineIndicator.boundingRect().height())]
175
                    for remainLineNo in remainLineNos:
176
                        xOverlap = False
177
                        yOverlap = False
178
                        extendDx = remainLineNo.size[0] if lineIndicator.isVH == 'V' else remainLineNo.size[1]
179
                        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)]
180
                        w, h = 0, 0                        
181
                        for x1 in range(reNoRect[0], reNoRect[2] + 1):
182
                            for x2 in range(liInRect[0], liInRect[2] + 1):
183
                                if x1 - x2 is 0:
184
                                    xOverlap = True
185
                                    w += 1
186
                        if not xOverlap:
187
                            continue
188
                        for y1 in range(reNoRect[1], reNoRect[3] + 1):
189
                            for y2 in range(liInRect[1], liInRect[3] + 1):
190
                                if y1 - y2 is 0:
191
                                    yOverlap = True
192
                                    h += 1
193

194
                        if not xOverlap or not yOverlap:
195
                            continue
196
                        overLapRate = (w / lineIndicator.boundingRect().width() * 100) if lineIndicator.boundingRect().width() > lineIndicator.boundingRect().height() else (h / lineIndicator.boundingRect().height() * 100)
197
                        #print(overLapRate)
198
                        if maxOverLap < overLapRate:
199
                            maxOverLap = overLapRate
200
                            matchLineNo = remainLineNo
201
                            #lineIndicator.setBrush(QBrush(QColor(255, 0, 0, 127)))
202

203
                    if matchLineNo is not None:
204
                        #print(matchLineNo.text() + ' indicator : ' + str(lineIndicator.boundingRect().x()) + ', ' + str(lineIndicator.boundingRect().y()))
205
                        matchLine = None
206
                        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])
207
                        #print([type(x1), type(y1), type(x2), type(y2)])
208
                        startXorY = (min(liInRect[0], liInRect[2]) - extendPixel) if lineIndicator.isVH == 'V' else (min(liInRect[1], liInRect[3]) - extendPixel)
209
                        endXorY = (max(liInRect[0], liInRect[2]) + extendPixel) if lineIndicator.isVH == 'V' else (max(liInRect[1], liInRect[3]) + extendPixel)
210
                        fXorY = []
211
                        for dXOrdY in range(startXorY, endXorY + 1): # if horizontal -> Y, if vertical -> X
212
                            if lineIndicator.isVH == 'V':
213
                                fXorY.append(round(((y2-y1)/(x2-x1))*dXOrdY + y1 - ((y2-y1)/(x2-x1))*x1)) # f(x)
214
                            else:
215
                                fXorY.append(round(((x2-x1)/(y2-y1))*dXOrdY + x1 - ((x2-x1)/(y2-y1))*y1)) # f(y)
216

217
                        #print(self._lines)
218
                        for line in self._lines:
219
                            axis1Overlap = False
220
                            axis2Overlap = False
221
                            lX1, lY1, lX2, lY2 = int(line.startPoint()[0]), int(line.startPoint()[1]), int(line.endPoint()[0]), int(line.endPoint()[1])
222
                            #print([lX1, lY1, lX2, lY2])
223

224
                            if lineIndicator.isVH == 'V':
225
                                range11 = range(startXorY, endXorY + 1)
226
                                range12 = range(min(lX1, lX2), max(lX1, lX2)+ 1)
227
                                range21 = fXorY
228
                                range22 = range(min(lY1, lY2), max(lY1, lY2) + 1)
229
                            else:
230
                                range11 = range(startXorY, endXorY + 1)
231
                                range12 = range(min(lY1, lY2), max(lY1, lY2) + 1)
232
                                range21 = fXorY
233
                                range22 = range(min(lX1, lX2), max(lX1, lX2) + 1)
234
                            #print(fXorY)
235
                            #print(type(fXorY[0]))
236
                            #print([range11[0],range11[-1], range12[0],range12[-1], range21[0],range21[-1], range22[0],range22[-1]])
237

238
                            for axis11 in range11:
239
                                for axis12 in range12:
240
                                    if axis11 - axis12 is 0:
241
                                        axis1Overlap = True
242
                                        break
243
                                if axis1Overlap: break
244
                            if not axis1Overlap:
245
                                continue
246
                            for axis21 in range21:
247
                                for axis22 in range22:
248
                                    if axis21 - axis22 is 0:
249
                                        axis2Overlap = True
250
                                        break
251
                                if axis2Overlap: break
252
                            
253
                            if axis1Overlap and axis2Overlap:
254
                                matchLine = line
255
                                break
256
                        if matchLine is not None:
257
                            matchLineNo.conns.append(matchLine)
258
                            lineIndicator.lineIndicator = 'Match'
259
                            lineIndicator.setBrush(QBrush(QColor(0, 0, 255, 127)))
260
                            #print('connected ' + matchLineNo.text() + ' and ' + matchLine.uid)                     
261
                ## up to here
262
                """
263
                            
264
                maxValue = len(self._lineNos) + 1   ## line no's count + secondary line
265

    
266
                # find primary lines
267
                for lineno in self._lineNos:
268
                    if displayMessage: displayMessage.emit('{} {}'.format(lineno.text(), 'Topology Construction'))
269
                    self.find_primary_lines(lineno)
270
                    if updateProgress: updateProgress.emit(maxValue)
271

    
272
                # find secondary lines
273
                lines = self._lines
274
                self.find_secondary_lines(lines)
275
                if updateProgress: updateProgress.emit(maxValue)
276

    
277
            ### make trim lines
278
            """
279
            updateProgress.emit(-1) # reset progressbar
280
            displayMessage.emit('Unknown line Topology Construction')
281
            orphanLines = [line for line in self._lines if line.owner is None] 
282
            if orphanLines:
283
                maxValue = len(orphanLines)
284
                orphanLines = sorted(orphanLines, key=lambda param:param.length(), reverse=True)
285
                while len(orphanLines) > 0:
286
                    trimLineNo = QEngineeringTrimLineNoTextItem()
287
                    trimLineNo.conns.append(orphanLines[0])
288
                    orphanLines[0].owner = trimLineNo
289
                    orphanLines[0].linkedItem = trimLineNo
290

291
                    connectedItems = self.findPrimaryLines(trimLineNo)
292
                    for item in connectedItems:
293
                        if item in orphanLines:
294
                            orphanLines.remove(item)
295
                            updateProgress.emit(maxValue)
296

297
                    self.findSecondaryLines(orphanLines)
298
                    for item in orphanLines[:]:
299
                        if item.owner is not None:
300
                            orphanLines.remove(item)
301
                            updateProgress.emit(maxValue)
302

303
                    docData.tracerLineNos.append(trimLineNo)
304
            """
305
            if updateProgress: updateProgress.emit(maxValue)
306
        except Exception as ex:
307
            from App import App 
308

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

    
312
    '''
313
        @brief      find objects connected to given line while loop
314
        @author     humkyung
315
        @date       2018.04.16
316
        @history    humkyung 2018.05.08 find symbol or line connected to given object
317
                    humkyung 2018.05.10 set found object's owner
318
                    humkyung 2018.05.17 try to connect both symbol and line
319
                    humkyung 2018.06.22 order connected objects
320
    '''
321
    def find_connected_objects(self, start, to=None):
322
        from EngineeringLineItem import QEngineeringLineItem
323
        from EngineeringEquipmentItem import QEngineeringEquipmentItem
324
        from SymbolSvgItem import SymbolSvgItem
325

    
326
        visited = [start]
327

    
328
        try:
329
            pool = []
330
            pool.append((0, start))
331
            
332
            while len(pool) > 0:
333
                sign, obj = pool.pop()
334

    
335
                """ check obj is upstream or downstream of spec break """
336
                matches = [spec_break for spec_break in self._spec_breaks if spec_break.is_connected(obj)]
337
                if matches or issubclass(type(obj), QEngineeringEquipmentItem): continue
338
                """ end loop if obj is to """
339
                if obj is to: break
340

    
341
                if type(obj) is QEngineeringLineItem:
342
                    symbolMatches = [x for x in self._symbols if (x.owner is None) and (x not in visited) and obj.is_connected(x)]
343
                    lineMatches = [x for x in self._lines if (x.owner is None) and (x is not obj) and (x not in visited) and obj.is_connected(x)]
344

    
345
                elif issubclass(type(obj), SymbolSvgItem):
346
                    lineMatches = [x for x in self._lines if (x.owner is None) and (x not in visited) and obj.is_connected(x)]
347

    
348
                    if len(lineMatches) > 1: # choose one if connected lines are more than 2
349
                        matches = [x for x in [visited[0], visited[-1]] if obj.is_connected(x)]
350
                        if matches:
351
                            next_connected = [x for x in lineMatches if obj.next_connected(x, matches[0])]
352
                            if next_connected: lineMatches = next_connected
353

    
354
                    symbolMatches = [x for x in self._symbols if (x.owner is None) and (x is not obj) and (x not in visited) and obj.is_connected(x, None)]
355

    
356
                # order connected objects
357
                matches = []
358
                matches.extend(symbolMatches)
359
                matches.extend(lineMatches)
360

    
361
                if sign == 0 and len(matches) > 1:
362
                    mid = int(len(matches)*0.5)
363
                    lhs = matches[0:mid]
364
                    rhs = matches[mid:]
365
                elif sign == -1:
366
                    lhs = matches
367
                    rhs = []
368
                else:
369
                    lhs = []
370
                    rhs = matches
371

    
372
                for match in lhs:
373
                    pool.append((-1, match))
374
                    visited.insert(0, match)
375

    
376
                for match in rhs:
377
                    pool.append((1, match))
378
                    visited.append(match)
379
                # up to here
380

    
381
        except Exception as ex:
382
            from App import App 
383

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

    
387
        return visited
388

    
389
'''
390
    @brief      connect attributes
391
    @author     humkyung
392
    @date       2018.06.17
393
    @history    humkyung 2018.06.21 paste connect attributes codes from recognizeLine function
394
                kyouho  2018.09.14  clear Item's owner 
395
'''
396
def connectAttrImpl(worker):
397
    from LineNoTracer import LineNoTracer
398
    from AppDocData import AppDocData
399
    from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
400
    from EngineeringInstrumentItem import QEngineeringInstrumentItem
401
    from EngineeringReducerItem import QEngineeringReducerItem
402
    from EngineeringEquipmentItem import QEngineeringEquipmentItem
403
    from QEngineeringOPCItem import QEngineeringOPCItem
404
    from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
405
    from EngineeringVendorItem import QEngineeringVendorItem
406

    
407
    try:
408
        symbols = []
409
        lines = []
410
        lineNos = []
411
        specBreak = []
412
        lineIndicator = []
413
        vendor = []
414
        for item in worker.graphicsView.scene.items():
415
            if type(item) is QEngineeringSpecBreakItem:
416
                specBreak.append(item)
417
            elif issubclass(type(item), SymbolSvgItem):
418
                symbols.append(item)
419
            elif type(item) is QEngineeringLineNoTextItem:
420
                lineNos.append(item)
421
            elif type(item) is QEngineeringLineItem:
422
                lines.append(item)
423
            elif type(item) is QEngineeringUnknownItem and item.lineIndicator != 'False':
424
                lineIndicator.append(item)
425
            elif type(item) is QEngineeringVendorItem:
426
                vendor.append(item)
427

    
428
        # trace line no
429
        tracer = LineNoTracer(symbols, lines, lineNos, specBreak, lineIndicator, vendor)
430
        tracer.execute(worker.displayMessage, worker.updateProgress)
431
        # up to here
432

    
433
        # connect attribute
434
        texts = [item for item in worker.graphicsView.scene.items() if issubclass(type(item), QEngineeringTextItem)]
435
        for symbol in symbols:
436
            try:
437
                symbol.connectAttribute(texts)
438
            except Exception as ex:
439
                from App import App 
440
                message = 'error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
441
                App.mainWnd().addMessage.emit(MessageType.Error, message)
442

    
443
        """ try to connect label to valve """
444
        labels = [symbol for symbol in symbols if type(symbol) is QEngineeringInstrumentItem and not symbol.is_connected]
445
        valves = [symbol for symbol in symbols if type(symbol) is not QEngineeringInstrumentItem and type(symbol) is not QEngineeringReducerItem and type(symbol) is not QEngineeringEquipmentItem \
446
            and type(symbol) is not QEngineeringOPCItem and type(symbol) is not QEngineeringSpecBreakItem]
447
        for valve in valves:
448
            valve.connectAttribute(labels, clear=False)
449

    
450
        # vendor package
451
        configs = AppDocData.instance().getConfigs('Supplied by Tag Rule', 'by Vendor')
452
        vendorTag = configs[0].value if configs else 'By Vendor'
453
        for vendorItem in vendor:
454
            for symbol in symbols:
455
                if vendorItem.includes(symbol):
456
                    matches = [prop for prop,value in symbol.properties.items() if prop.Attribute == 'Supplied By']
457
                    if matches: symbol.properties[matches[0]] = vendorTag
458
                else:
459
                    matches = [prop for prop,value in symbol.properties.items() if prop.Attribute == 'Supplied By']
460
                    if matches: symbol.properties[matches[0]] = ''
461
        # up to here
462
    except Exception as ex:
463
        from App import App 
464
        message = 'error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
465
        App.mainWnd().addMessage.emit(MessageType.Error, message)
466
    finally:
467
        worker.finished.emit()
클립보드 이미지 추가 (최대 크기: 500 MB)