프로젝트

일반

사용자정보

통계
| 개정판:

hytos / DTI_PID / DTI_PID / LineNoTracer.py @ 27d414f8

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

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

    
4
import sys
5
import math
6
import asyncio
7
import shapely
8
from AppDocData import AppDocData, MessageType
9
from EngineeringLineItem import QEngineeringLineItem
10
from EngineeringLineNoTextItem import QEngineeringLineNoTextItem
11
from QEngineeringFlowArrowItem import QEngineeringFlowArrowItem
12
from SymbolSvgItem import SymbolSvgItem
13
from EngineeringTextItem import QEngineeringTextItem
14
from EngineeringUnknownItem import QEngineeringUnknownItem
15
from EngineeringNozzleItem import QEngineeringNozzleItem
16

    
17
try:
18
    from PyQt5.QtCore import *
19
    from PyQt5.QtGui import *
20
    from PyQt5.QtWidgets import *
21
except ImportError:
22
    try:
23
        from PyQt4.QtCore import *
24
        from PyQt4.QtGui import *
25
    except ImportError:
26
        raise ImportError("ImageViewerQt: Requires PyQt5 or PyQt4.")
27

    
28

    
29
class LineNoTracer:
30
    '''
31
        @history    2018.04.26 Jeongwoo Variable name changed (texts → lineNos)
32
    '''
33

    
34
    def __init__(self, symbols, lines, lineNos, specBreaks, lineIndicators, vendors, end_breaks):
35
        try:
36
            self._symbols = symbols
37
            self._lines = lines
38
            self._lineNos = lineNos
39
            self._spec_breaks = specBreaks
40
            self._lineIndicator = lineIndicators
41
            self._end_breaks = end_breaks
42
            self.maxValue = None
43

    
44
        except Exception as ex:
45
            from App import App
46

    
47
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
48
                                                           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

    
56
    def find_primary_lines(self, lineno, include_signal=True):
57
        from EngineeringLineItem import QEngineeringLineItem
58
        from EngineeringRunItem import QEngineeringRunItem
59

    
60
        connected_items = []
61

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

    
79
        # print(connected_items)
80
        if connected_items:
81
            for item in connected_items:
82
                item.owner = lineno  # set item's owner
83

    
84
            line_run = QEngineeringRunItem()
85
            line_run.items = connected_items
86
            if line_run.arrange_flow_direction():
87
                line_run._fixed = True
88
            else:
89
                line_run._fixed = False
90
            line_run.owner = lineno
91
            lineno.runs.append(line_run)
92

    
93
            lineno.set_property('From', connected_items[0])
94
            lineno.set_property('To', connected_items[-1])
95

    
96
        if _to is not None and connected_items and connected_items[-1] is not _to:
97
            _to.owner = None
98

    
99
        return connected_items
100

    
101
    '''
102
        @brief  find secondary lines
103
        @author humkyung
104
    '''
105

    
106
    def find_secondary_lines(self, lines_and_symbols, include_signal=True, is_trim=False):
107
        from EngineeringAbstractItem import QEngineeringAbstractItem
108
        from EngineeringLineItem import QEngineeringLineItem
109
        from EngineeringRunItem import QEngineeringRunItem
110
        from QEngineeringTrimLineNoTextItem import QEngineeringTrimLineNoTextItem
111

    
112
        try:
113
            foundCount = 1
114
            while foundCount:
115
                foundCount = 0
116
                notMatches = []
117
                for line in lines_and_symbols:
118
                    if line.owner is not None: continue
119

    
120
                    if not include_signal and type(line) is QEngineeringLineItem and not line.is_piping(): continue
121

    
122
                    end_break_components = []
123
                    if type(line) is QEngineeringLineItem:
124
                        for end_break in self._end_breaks:
125
                            if line is end_break.owner:
126
                                end_break_components.append(end_break.prop('Connected Item'))
127
                            elif line is end_break.prop('Connected Item'):
128
                                end_break_components.append(end_break.owner)
129

    
130
                    if not is_trim:
131
                        line_matches = [x for x in self._lines if x.owner and \
132
                                            line.is_connected(x, QEngineeringAbstractItem.CONNECTED_AT_BODY) and x not in end_break_components]
133
                        symbol_matches = [x for x in self._symbols if x.owner and line.is_connected(x)]# and x.canBeSecondary(line)]
134
                    else:
135
                        line_matches = [x for x in self._lines if x.owner and type(x.owner) is QEngineeringTrimLineNoTextItem and \
136
                                            line.is_connected(x, QEngineeringAbstractItem.CONNECTED_AT_BODY) and x not in end_break_components]
137
                        symbol_matches = [x for x in self._symbols if x.owner and type(x.owner) is QEngineeringTrimLineNoTextItem and line.is_connected(x)]# and x.canBeSecondary(line)]
138

    
139
                    if line_matches or symbol_matches:
140
                        foundCount += 1
141
                        connected_items = self.find_connected_objects(line, include_signal=include_signal)
142

    
143
                        owner = line_matches[0].owner if line_matches else symbol_matches[0].owner
144
                        for item in connected_items:
145
                            item.owner = owner  # set item's owner
146

    
147
                        if connected_items:
148
                            line_run = QEngineeringRunItem()
149
                            line_run.items = connected_items
150
                            fixed = line_run.arrange_flow_direction()
151
                            if line_run.items is not None and len(line_run.items) > 0:
152
                                if fixed:
153
                                    line_run._fixed = True
154
                                else:
155
                                    line_run._fixed = False
156
                                line_run.owner = owner
157
                                owner.runs.append(line_run)
158
                    else:
159
                        notMatches.append(line)
160
                # lines_and_symbols = notMatches
161
        except Exception as ex:
162
            from App import App
163

    
164
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
165
                                                           sys.exc_info()[-1].tb_lineno)
166
            App.mainWnd().addMessage.emit(MessageType.Error, message)
167

    
168
    '''
169
        @brief      trace line no
170
        @author     humkyung
171
        @date       2018.04.16
172
        @history    2018.04.26 Jeongwoo docDatalineNos = self.lineNos, Not For loop
173
                    humkyung 2018.05.08 add flow arrow
174
                    Jeongwoo 2018.05.14 Add [runs] on Primary/Secondary Line - Need to modify Secondary Line
175
                    Jeongwoo 2018.05.15 Make Comments [lineno.conns[0].owner = lineno]
176
                    Jeongwoo 2018.05.17 Modify find secondary lines with 'while'
177
                                        Modify find secondary lines with 'while' used sublist for unconnected line
178
                    humkyung 2018.05.18 set start line's owner before tracing
179
    '''
180

    
181
    def execute(self, displayMessage, updateProgress, toler=600):
182
        from EngineeringLineItem import QEngineeringLineItem
183
        from SymbolSvgItem import SymbolSvgItem
184
        from EngineeringRunItem import QEngineeringRunItem
185
        from QEngineeringTrimLineNoTextItem import QEngineeringTrimLineNoTextItem
186
        from EngineeringEquipmentItem import QEngineeringEquipmentItem
187

    
188
        try:
189
            app_doc_data = AppDocData.instance()
190

    
191
            configs = app_doc_data.getConfigs('Line No', 'Delimiter')
192
            if 1 == len(configs):
193
                #configs = app_doc_data.getConfigs('Line No', 'Configuration')
194

    
195
                '''
196
                for line_no in self._lineNos:
197
                    _from = line_no.prop('From')
198
                    _to = line_no.prop('To')
199

200
                    line_no.conns.clear()
201
                    minDist = None
202
                    startLine = None
203
                    for line in [line for line in self._lines if line.owner is None and line.is_piping(strong=True)]:
204
                        dist = line.distanceTo((line_no.center().x(), line_no.center().y()))
205
                        if (minDist is None) or (dist < minDist):
206
                            minDist = dist
207
                            startLine = line
208
                    if (startLine is not None) and (minDist < toler):
209
                        line_no.conns.append(startLine)
210
                        if not _from or not _to:
211
                            startLine.owner = line_no
212

213
                    if _from and _to and \
214
                            (type(_from) is QEngineeringLineItem or issubclass(type(_from), SymbolSvgItem)) and \
215
                            (type(_to) is QEngineeringLineItem or issubclass(type(_to), SymbolSvgItem)):
216
                        _from.owner = line_no
217
                        _to.owner = line_no
218
                        line_no._fixed = True
219
                    elif _from and not _to and (type(_from) is QEngineeringLineItem or issubclass(type(_from), SymbolSvgItem)):
220
                        _from.owner = line_no
221
                        line_no.set_property('To', None)
222
                        line_no._fixed = True if len([conn for conn in _from.connectors if conn.connectedItem]) == 1 else False
223
                    else:
224
                        line_no.set_property('From', None)
225
                        line_no.set_property('To', None)
226
                        line_no._fixed = False
227

228
                # find primary lines
229
                # sort line no with from,to value
230
                self._lineNos.sort(
231
                    key=lambda line_no: (1 if line_no.prop('From') else 0) + (1 if line_no.prop('To') else 0),
232
                    reverse=True)
233
                for lineno in self._lineNos:
234
                    if displayMessage: displayMessage.emit('{} {}'.format(lineno.text(), 'Topology Construction'))
235
                    self.find_primary_lines(lineno, include_signal=False)
236
                    if updateProgress: updateProgress.emit(self.maxValue)
237
                '''
238

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

    
241
                # find primary lines
242
                remainLineNo1 = []
243
                remainLineNo2 = []
244
                for line_no in self._lineNos:
245
                    line_no.conns.clear()
246

    
247
                    _from = line_no.prop('From')
248
                    _to = line_no.prop('To')
249

    
250
                    if _from and _to:
251
                        _from.owner = line_no
252
                        _to.owner = line_no
253
                        line_no._fixed = True
254
                    else:
255
                        remainLineNo1.append(line_no)
256
                
257
                for line_no in remainLineNo1:
258
                    _from = line_no.prop('From')
259
                    _to = line_no.prop('To')
260
                    
261
                    if _from:
262
                        _from.owner = line_no
263
                        line_no.set_property('To', None)
264
                        line_no._fixed = True if len([conn for conn in _from.connectors if conn.connectedItem]) == 1 else False
265
                    else:
266
                        remainLineNo2.append(line_no)
267

    
268
                for line_no in remainLineNo2:
269
                    line_no.set_property('From', None)
270
                    line_no.set_property('To', None)
271
                    line_no._fixed = False
272
                    
273
                remainVertical = []
274
                remainVertical2 = []
275
                remainHorizontal = []
276
                for line_no in remainLineNo2:
277
                    if line_no.angle == 0:
278
                        remainHorizontal.append(line_no)
279
                    else:
280
                        remainVertical.append(line_no)
281

    
282
                while remainVertical:
283
                    remainVerticalLine = [line for line in self._lines if line.owner is None and line.is_piping(strong=True) and line.isVertical()]
284
                    if not remainVerticalLine:
285
                        remainVertical2.extend(remainVertical)
286
                        break
287

    
288
                    minDist = None
289
                    startLine = None
290
                    vertical = None
291
                    
292
                    for line_no in remainVertical:
293
                        point = (line_no.center().x(), line_no.center().y())
294
                        remainVerticalLine.sort(key=lambda x: x.distanceTo(point))
295

    
296
                        dist = remainVerticalLine[0].distanceTo(point)
297
                        if (minDist is None) or (dist < minDist):
298
                            minDist = dist
299
                            startLine = remainVerticalLine[0]
300
                            vertical = line_no
301

    
302
                    if (startLine is not None) and (minDist < toler):
303
                        vertical.conns.append(startLine)
304
                        startLine.owner = vertical
305
                    else:
306
                        remainVertical2.append(vertical)
307

    
308
                    if vertical:
309
                        remainVertical.remove(vertical)
310
                    else:
311
                        break
312

    
313
                while remainHorizontal:
314
                    remainLine = [line for line in self._lines if line.owner is None and line.is_piping(strong=True)]
315
                    if not remainLine:
316
                        break
317

    
318
                    minDist = None
319
                    startLine = None
320
                    horizontal = None
321
                    
322
                    for line_no in remainHorizontal:
323
                        point = (line_no.center().x(), line_no.center().y())
324
                        remainLine.sort(key=lambda x: x.distanceTo(point))
325

    
326
                        dist = remainLine[0].distanceTo(point)
327
                        if (minDist is None) or (dist < minDist):
328
                            minDist = dist
329
                            startLine = remainLine[0]
330
                            horizontal = line_no
331

    
332
                    if (startLine is not None) and (minDist < toler):
333
                        horizontal.conns.append(startLine)
334
                        startLine.owner = horizontal
335

    
336
                    if horizontal:
337
                        remainHorizontal.remove(horizontal)
338
                    else:
339
                        break
340

    
341
                while remainVertical2:
342
                    remainLine = [line for line in self._lines if line.owner is None and line.is_piping(strong=True)]
343
                    if not remainLine:
344
                        break
345

    
346
                    minDist = None
347
                    startLine = None
348
                    remain = None
349
                    
350
                    for line_no in remainVertical2:
351
                        point = (line_no.center().x(), line_no.center().y())
352
                        remainLine.sort(key=lambda x: x.distanceTo(point))
353

    
354
                        dist = remainLine[0].distanceTo(point)
355
                        if (minDist is None) or (dist < minDist):
356
                            minDist = dist
357
                            startLine = remainLine[0]
358
                            remain = line_no
359

    
360
                    if (startLine is not None) and (minDist < toler):
361
                        remain.conns.append(startLine)
362
                        startLine.owner = remain
363

    
364
                    else:
365
                        remainVertical2.append(remain)
366

    
367
                    if remain:
368
                        remainVertical.remove(remain)
369
                    else:
370
                        break
371

    
372
                # sort line no with from,to value
373
                self._lineNos.sort(
374
                    key=lambda line_no: (1 if line_no.prop('From') else 0) + (1 if line_no.prop('To') else 0),
375
                    reverse=True)
376
                for lineno in self._lineNos:
377
                    if displayMessage: displayMessage.emit('{} {}'.format(lineno.text(), 'Topology Construction'))
378
                    self.find_primary_lines(lineno, include_signal=False)
379
                    if updateProgress: updateProgress.emit(self.maxValue)
380
                # up to here
381

    
382
                # find secondary lines
383
                lines_and_symbols = self._lines + [sym for sym in self._symbols if not issubclass(type(sym), QEngineeringEquipmentItem)]
384
                self.find_secondary_lines(lines_and_symbols, include_signal=False)
385

    
386
                # double check conn line cuz startLine may need at first step
387
                for lineno in self._lineNos:
388
                    lineno.conns.clear()
389
                    minDist = None
390
                    startLine = None
391
                    if len(lineno.runs) is 0:
392
                        continue
393
                    for line in [line for line in lineno.runs[0].items if
394
                                 type(line) is QEngineeringLineItem and line.is_piping(strong=True)]:
395
                        dist = line.distanceTo((lineno.center().x(), lineno.center().y()))
396
                        if (minDist is None) or (dist < minDist):
397
                            minDist = dist
398
                            startLine = line
399
                    if (startLine is not None):  # and (minDist < toler):
400
                        lineno.conns.append(startLine)
401

    
402
                if updateProgress: updateProgress.emit(self.maxValue)
403

    
404
            # make trim lines
405
            updateProgress.emit(-1)  # reset progressbar
406
            displayMessage.emit('Unknown line Topology Construction')
407
            orphanLines = [line for line in self._lines if line.owner is None]
408
            orphanSymbols = [symbol for symbol in self._symbols if
409
                             symbol.owner is None and not issubclass(type(symbol), QEngineeringEquipmentItem)]
410
            if orphanLines + orphanSymbols:
411
                self.maxValue = len(orphanLines) + 1
412
                orphanLines = sorted(orphanLines, key=lambda param: param.length(), reverse=True)
413
                orphans = orphanLines + orphanSymbols
414
                while len(orphans) > 0:
415
                    trimLineNo = QEngineeringTrimLineNoTextItem()
416
                    trimLineNo.conns.append(orphans[0])
417
                    orphans[0].owner = trimLineNo
418
                    # orphanLines[0].linkedItem = trimLineNo
419

    
420
                    connectedItems = self.find_primary_lines(trimLineNo)
421
                    for item in connectedItems:
422
                        if item in orphans:
423
                            orphans.remove(item)
424
                            updateProgress.emit(self.maxValue)
425

    
426
                    self.find_secondary_lines(orphans, is_trim=True)
427
                    for item_index in reversed(range(len(orphans))):
428
                        item = orphans[item_index]
429
                        if item.owner is not None:
430
                            orphans.remove(item)
431
                            updateProgress.emit(self.maxValue)
432

    
433
                    app_doc_data.tracerLineNos.append(trimLineNo)
434

    
435
            if updateProgress: updateProgress.emit(self.maxValue)
436
        except Exception as ex:
437
            from App import App
438

    
439
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
440
                                                           sys.exc_info()[-1].tb_lineno)
441
            App.mainWnd().addMessage.emit(MessageType.Error, message)
442

    
443
    '''
444
        @brief      find objects connected to given line while loop
445
        @author     humkyung
446
        @date       2018.04.16
447
        @history    humkyung 2018.05.08 find symbol or line connected to given object
448
                    humkyung 2018.05.10 set found object's owner
449
                    humkyung 2018.05.17 try to connect both symbol and line
450
                    humkyung 2018.06.22 order connected objects
451
    '''
452
    def find_connected_objects(self, start, to=None, primary=False, include_signal=True):
453
        from EngineeringLineItem import QEngineeringLineItem
454
        from EngineeringEquipmentItem import QEngineeringEquipmentItem
455
        from EngineeringInstrumentItem import QEngineeringInstrumentItem
456
        from SymbolSvgItem import SymbolSvgItem
457

    
458
        visited = [start]
459
        break_at_first = None
460

    
461
        try:
462
            pool = []
463
            pool.append((0, start))
464

    
465
            while len(pool) > 0:
466
                sign, obj = pool.pop()
467

    
468
                # """ check obj is upstream or downstream of spec break """
469
                # matches = [spec_break for spec_break in self._spec_breaks if spec_break.is_connected(obj)]
470
                # if matches or issubclass(type(obj), QEngineeringEquipmentItem):
471
                #    visited.pop(visited.index(obj))
472
                #    continue
473
                # print('obj={} pool={}'.format(obj, pool))
474

    
475
                match = False
476
                if not primary:
477
                    for end_break in self._end_breaks:
478
                        if obj is end_break.owner or obj is end_break.prop('Connected Item'):
479
                            if end_break.owner.is_connected(end_break.prop('Connected Item')) and \
480
                                    end_break.prop('Connected Item').is_connected(end_break.owner) and \
481
                                    len(visited) is not 1:
482
                                match = True
483
                                if obj.is_connected(break_at_first):
484
                                    visited.pop(visited.index(obj))
485
                                break
486
                            elif end_break.owner.is_connected(end_break.prop('Connected Item')) and \
487
                                    end_break.prop('Connected Item').is_connected(end_break.owner):
488
                                break_at_first = obj
489
                                break
490

    
491
                if issubclass(type(obj), QEngineeringEquipmentItem):
492
                    visited.pop(visited.index(obj))
493
                    continue
494
                elif match:
495
                    continue
496

    
497
                """ end loop if obj is to """
498
                if to is not None and str(obj.uid) == str(to.uid): break
499

    
500
                # nextmatches list always has one item
501
                if type(obj) is QEngineeringLineItem:
502
                    symbolMatches = [x for x in self._symbols if (x.owner is None or x.owner == start.owner) and (
503
                            x not in visited) and obj.is_connected(x)]
504
                    if include_signal:
505
                        lineMatches = [x for x in self._lines if
506
                                       (x.owner is None or x.owner == start.owner) and (x is not obj) and (
507
                                               x not in visited) and obj.is_connected(x)]
508
                    else:
509
                        lineMatches = [x for x in self._lines if
510
                                       x.is_piping() and (x.owner is None or x.owner == start.owner) and (
511
                                               x is not obj) and (x not in visited) and obj.is_connected(x)]
512
                    nextMatches = symbolMatches + lineMatches
513

    
514
                elif issubclass(type(obj), SymbolSvgItem):
515
                    # symbol can be connected with line and another symbol at the same time
516
                    if include_signal:
517
                        lineMatches = [x for x in self._lines if (x.owner is None or x.owner == start.owner) and (
518
                                x not in visited) and obj.is_connected(x)]
519
                    else:
520
                        lineMatches = [x for x in self._lines if
521
                                       x.is_piping() and (x.owner is None or x.owner == start.owner) and (
522
                                               x not in visited) and obj.is_connected(x)]
523
                    symbolMatches = [x for x in self._symbols if
524
                                     (x.owner is None or x.owner == start.owner) and (x is not obj) and (
525
                                             x not in visited) and obj.is_connected(x)]
526
                    nextMatches = symbolMatches + lineMatches
527

    
528
                    if len(visited) > 1: # symbol must pass straight, choose one if connected items are more than 2
529
                        matches = [x for x in visited if obj.is_connected(x)]
530
                        if matches:
531
                            next_connected = [x for x in nextMatches if obj.next_connected(x, matches[0])]
532

    
533
                            if next_connected:
534
                                nextMatches = next_connected
535
                            else:
536
                                #nextMatches = [nextMatches[0]]
537
                                nextMatches = []
538

    
539
                    '''
540
                    if len(visited) > 1 and len(nextMatches) > 1: # symbol must pass straight, choose one if connected items are more than 2
541
                        matches = [x for x in visited if obj.is_connected(x)]
542
                        if matches:
543
                            next_connected = [x for x in nextMatches if obj.next_connected(x, matches[0])]
544

545
                            if next_connected:
546
                                nextMatches = next_connected
547
                            else:
548
                                #nextMatches = [nextMatches[0]]
549
                                nextMatches = []
550
                    else:
551
                        if len(nextMatches) > 1: # select by connection index
552
                            next_connected = [conn.connectedItem for conn in obj.connectors if conn.connectedItem and conn.connectedItem in nextMatches]
553
                            if next_connected:
554
                                nextMatches = [next_connected[0]]
555
                            else:
556
                                nextMatches = []
557
                    '''
558

    
559
                    # if obj symbol has break connector and nextMatch connected that connector than break line group
560
                    if nextMatches and obj.break_connector and [index for index in obj.break_connector if
561
                                                                obj.connectors[index].connectedItem is nextMatches[0]]:
562
                        match = True
563
                        break
564

    
565
                # if obj item connected symbol that has break connector then break line group
566
                if nextMatches:
567
                    pop_index = []
568
                    index = 0
569
                    for nextMatch in nextMatches:
570
                        if hasattr(nextMatch, 'break_connector'):
571
                            if [index for index in nextMatch.break_connector if
572
                                nextMatch.connectors[index].connectedItem is obj]:
573
                                pop_index.append(index)
574
                        index += 1
575

    
576
                    for index in reversed(pop_index):
577
                        nextMatches.pop(index)
578

    
579
                # order connected objects
580
                matches = []
581
                matches.extend(nextMatches)
582

    
583
                if sign == 0 and len(matches) > 1:
584
                    mid = int(len(matches) * 0.5)
585
                    lhs = matches[0:mid]
586
                    rhs = matches[mid:]
587
                elif sign == -1:
588
                    lhs = matches
589
                    rhs = []
590
                else:
591
                    lhs = []
592
                    rhs = matches
593

    
594
                for match in lhs:
595
                    # print(match)
596
                    pool.append((-1, match))
597
                    visited.insert(0, match)
598

    
599
                for match in rhs:
600
                    # print(match)
601
                    pool.append((1, match))
602
                    visited.append(match)
603
                # up to here
604

    
605
        except Exception as ex:
606
            from App import App
607

    
608
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
609
                                                           sys.exc_info()[-1].tb_lineno)
610
            App.mainWnd().addMessage.emit(MessageType.Error, message)
611

    
612
        # print(visited)
613
        return visited
614

    
615

    
616
'''
617
    @brief      connect attributes
618
    @author     humkyung
619
    @date       2018.06.17
620
    @history    humkyung 2018.06.21 paste connect attributes codes from recognizeLine function
621
                kyouho  2018.09.14  clear Item's owner 
622
'''
623
async def connectAttrImpl(worker, update_line_type, update_flow_mark, update_spec, update_stream_no):
624
    from App import App
625
    import uuid
626
    from LineNoTracer import LineNoTracer
627
    from AppDocData import AppDocData
628
    from EngineeringAbstractItem import QEngineeringAbstractItem
629
    from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
630
    from EngineeringInstrumentItem import QEngineeringInstrumentItem
631
    from EngineeringReducerItem import QEngineeringReducerItem
632
    from EngineeringEquipmentItem import QEngineeringEquipmentItem
633
    from QEngineeringOPCItem import QEngineeringOPCItem
634
    from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
635
    from EngineeringVendorItem import QEngineeringVendorItem
636
    from EngineeringEndBreakItem import QEngineeringEndBreakItem
637
    from EngineeringFlowMarkItem import QEngineeringFlowMarkItem
638
    from EngineeringReservedWordTextItem import QEngineeringReservedWordTextItem
639
    from QEngineeringSizeTextItem import QEngineeringSizeTextItem
640
    from EngineeringValveOperCodeTextItem import QEngineeringValveOperCodeTextItem
641
    from QEngineeringTrimLineNoTextItem import QEngineeringTrimLineNoTextItem
642
    from QEngineeringTagNoTextItem import QEngineeringTagNoTextItem
643
    from EngineeringErrorItem import QEngineeringErrorItem
644
    from EngineeringTextItem import QEngineeringTextItem
645
    from SpecialItemTypesDialog import SpecialItemTracer
646

    
647
    try:
648
        docdata = AppDocData.instance()
649
        worker.display_message.emit('Initializing...')
650

    
651
        ALL_ITEM = [item for item in worker.scene.items() if issubclass(type(item), QEngineeringAbstractItem)]
652
        for item in ALL_ITEM:
653
            item.setVisible(False)
654
        QApplication.processEvents()
655

    
656
        symbols = []
657
        lines = [item for item in worker.scene.items() if type(item) is QEngineeringLineItem]
658
        for line in lines:
659
            line._skip = False
660
        lineNos = []
661
        spec_breaks = []
662
        lineIndicator = []
663
        vendor_packages = [item for item in worker.scene.items() if
664
                           type(item) is QEngineeringVendorItem and item.pack_type == 'Vendor Package']
665
        equip_packages = [item for item in worker.scene.items() if
666
                          type(item) is QEngineeringVendorItem and item.pack_type == 'Equipment Package']
667
        end_breaks = []
668
        notes = []
669
        flow_marks = []
670

    
671
        for error_item in [item for item in worker.scene.items() if type(item) is QEngineeringErrorItem]:
672
            error_item.transfer.onRemoved.emit(error_item)
673

    
674
        for end_break in [item for item in worker.scene.items() if type(item) is QEngineeringEndBreakItem]:
675
            if not end_break.prop('Freeze'):
676
                end_break.transfer.onRemoved.emit(end_break)
677
            else:
678
                end_breaks.append(end_break)
679

    
680
        QApplication.processEvents()
681

    
682
        '''
683
        for flow_mark in [item for item in worker.scene.items() if type(item) is QEngineeringFlowMarkItem]:
684
            if not flow_mark.prop('Freeze'):
685
                flow_mark.transfer.onRemoved.emit(flow_mark)
686
            else:
687
                flow_marks.append(flow_mark)
688
        '''
689

    
690
        configs = docdata.getConfigs('Supplied by Tag Rule', 'by Vendor')
691
        vendorTag = configs[0].value if configs else 'By Vendor'
692
        for item in worker.scene.items():
693
            if type(item) is QEngineeringSpecBreakItem:
694
                spec_breaks.append(item)
695
            elif issubclass(type(item), SymbolSvgItem) and not (type(item) is QEngineeringErrorItem) and not (
696
                    type(item) is QEngineeringUnknownItem) and item.type != 'Notes' and not (
697
                    type(item) is QEngineeringEndBreakItem):
698
                matches = [vendor_package for vendor_package in vendor_packages if vendor_package.includes(item)]
699
                if matches:
700
                    item.set_property('Supplied By', vendorTag)
701
                else:
702
                    item.set_property('Supplied By', '')
703
                symbols.append(item)
704
            elif type(item) is QEngineeringLineNoTextItem:
705
                lineNos.append(item)
706
            elif type(item) is QEngineeringLineItem:
707
                # matches = [vendor_package for vendor_package in vendor_packages if vendor_package.includes(item)]
708
                # if not matches: lines.append(item)
709
                # for check line disappear bug
710
                pass
711
                # lines.append(item)
712
            elif type(item) is QEngineeringUnknownItem and item.lineIndicator != 'False':
713
                lineIndicator.append(item)
714
            elif issubclass(type(item), QEngineeringTextItem):
715
                item.owner = None
716
            elif item.type == 'Notes':
717
                notes.append(item)
718

    
719
        QApplication.processEvents()
720

    
721
        # remove unknown line's
722
        pastTrim = docdata.tracerLineNos
723
        treeWidget = App.mainWnd().itemTreeWidget
724
        for pastTrimIndex in reversed(range(len(pastTrim))):
725
            if type(pastTrim[pastTrimIndex]) is QEngineeringTrimLineNoTextItem:
726
                try:
727
                    connected_items = pastTrim[pastTrimIndex].getConnectedItems()
728
                    for item in connected_items:
729
                        treeWidget.addTreeItem(treeWidget.SymbolsTreeItem, item)
730
                    pastTrim[pastTrimIndex].explode()
731
                finally:
732
                    pass
733

    
734
        # trace line no
735
        tracer = LineNoTracer(symbols, lines, lineNos, spec_breaks, lineIndicator, vendor_packages, end_breaks)
736
        tracer.execute(worker.display_message, worker.updateProgress)
737
        # up to here
738

    
739
        # connect attribute
740
        worker.display_message.emit('Connecting Attribute...')
741
        QApplication.processEvents()
742
        texts = [item for item in worker.scene.items() if issubclass(type(item), QEngineeringTextItem)]
743
        for symbol in symbols:
744
            try:
745
                if symbol.iType == 18 or symbol.iType == 31: # opc will be checked later
746
                    symbol.clear_attr_and_assoc_item()
747
                    continue
748
                symbol.connectAttribute(texts)
749
            except Exception as ex:
750
                message = 'error occurred({}) in {}:{}'.format(repr(ex), sys.exc_info()[-1].tb_frame.f_code.co_filename,
751
                                                               sys.exc_info()[-1].tb_lineno)
752
                worker.display_message.emit(message)
753

    
754
        '''
755
        # clear line
756
        for line in lines:
757
            try:
758
                line.clear_attr_and_assoc_item()
759
            except Exception as ex:
760
                message = 'error occurred({}) in {}:{}'.format(repr(ex), sys.exc_info()[-1].tb_frame.f_code.co_filename,
761
                                                               sys.exc_info()[-1].tb_lineno)
762
                worker.display_message.emit(message)
763
        '''
764

    
765
        """ try to connect label to valve """
766
        labels = [symbol for symbol in symbols if symbol.iType == 19]  # Labels - Symbol => Instrument
767
        valves = [symbol for symbol in symbols if
768
                  symbol.iType == 34 or symbol.iType == 17 or symbol.iType == 22]  # Specialty Components, In-Line, Relief Devices
769
        for label in labels:
770
            label.connectAttribute(valves, clear=False)
771

    
772
        # for slope, etc.., symbol to line
773
        labels = [symbol for symbol in symbols if symbol.iType == 30]  # Labels - Line => Piping
774
        for label in labels:
775
            label.connectAttribute(lines, clear=False)
776

    
777
        '''
778
        # for alarm, symbol to symbol : no more used
779
        labels = [symbol for symbol in symbols if symbol.iType == 29]  # Labels - Piping Components => Instrument
780
        valves = [symbol for symbol in symbols if symbol.iType == 25]  # System Functions
781
        for label in labels:
782
            label.connectAttribute(valves, clear=False)
783
        '''
784

    
785
        QApplication.processEvents()
786
        for symbol in symbols:
787
            for assoc in symbol.associations():
788
                if assoc.owner is None and not issubclass(type(assoc), SymbolSvgItem):
789
                    assoc.owner = symbol
790

    
791
        """ restore note text item owner """
792
        for note in notes:
793
            for noteText in note.associations():
794
                noteText.owner = note
795

    
796
        """ try to find text item's owner """
797
        texts = [item for item in worker.scene.items() if type(item) is QEngineeringReservedWordTextItem]
798
        for text in texts:
799
            text.findOwner(lines)
800

    
801
        QApplication.processEvents()
802
        # restore and save manual edited attr's text item
803
        texts = [item for item in worker.scene.items() if issubclass(type(item), QEngineeringTextItem)]
804
        targetText = []
805
        for text in texts:
806
            if text.owner:
807
                continue
808
            found = False
809
            for symbol in symbols + lines:
810
                for attr in list(symbol.attrs.keys()):
811
                    # skip freezed attr or already consumed by symbol
812
                    if attr.AssocItem == text:
813
                        text.owner = symbol
814
                        found = True
815
                        break
816
                    # restore missing attr
817
                    elif type(attr.AssocItem) is str or type(attr.AssocItem) is uuid.UUID:
818
                        if str(attr.AssocItem) == str(text.uid):
819
                            symbol.add_assoc_item(text)
820
                            attr.AssocItem = text
821
                            text.owner = symbol
822
                            found = True
823
                        break
824
                if found: break
825
            if not found and (
826
                    type(text) is QEngineeringSizeTextItem or type(text) is QEngineeringValveOperCodeTextItem or \
827
                    type(text) is QEngineeringTagNoTextItem or type(text) is QEngineeringTextItem):
828
                targetText.append(text)
829

    
830
        # check opc
831
        for symbol in symbols:
832
            try:
833
                if symbol.iType == 18 or symbol.iType == 31:
834
                    symbol.connectAttribute(texts)
835
            except Exception as ex:
836
                message = 'error occurred({}) in {}:{}'.format(repr(ex), sys.exc_info()[-1].tb_frame.f_code.co_filename,
837
                                                               sys.exc_info()[-1].tb_lineno)
838
                worker.display_message.emit(message)
839

    
840
        targetText = sorted(targetText, key=lambda param: len(param.text()), reverse=True)
841
        for symbol in symbols + equip_packages:
842
            symbol._skip = False
843
        for text in targetText:
844
            ret = text.findOwner(symbols) if type(text) is not QEngineeringTagNoTextItem else text.findOwner(
845
                symbols + equip_packages)
846
            if ret:
847
                worker.add_update_text.emit(text, ret)
848

    
849
        # need fix
850
        #for text in targetText: # find line size
851
        #    if type(text) is QEngineeringSizeTextItem and not text.owner:
852
        #        text.findOwner(lines)
853

    
854
        """ update line type """
855
        if update_line_type:
856
            worker.display_message.emit('Updating Line Type...')
857
            for line in lines:
858
                line.update_line_type()
859

    
860
        QApplication.processEvents()
861
        """ make end break """
862
        make_end_break(worker, end_breaks, lineNos)
863

    
864
        QApplication.processEvents()
865
        """ update spec break """
866
        if update_spec:
867
            make_spec(worker, spec_breaks, lines, lineNos)
868

    
869
        worker.updateProgress.emit(tracer.maxValue)
870

    
871
        # trace special item
872
        worker.display_message.emit('Finding line for special item...')
873
        QApplication.processEvents()
874
        tracer = SpecialItemTracer([item for item in worker.scene.items() if
875
                                    (type(item) is SymbolSvgItem or type(item) is QEngineeringTextItem) and
876
                                    item.special_item_type], lines)
877
        tracer.execute(worker.display_message, worker.updateProgress)
878
        # up to here
879

    
880
        ''' sort run flow order '''
881
        worker.display_message.emit('Sorting Lines...')
882
        QApplication.processEvents()
883
        sort_run_flow(worker)
884

    
885
        QApplication.processEvents()
886
        """ make flow mark """
887
        if update_flow_mark:
888
            make_flow_mark(worker, lines)
889

    
890
        ''' get line no's from/to equipment '''
891
        for lineNo in lineNos:
892
            lineNo.clear_attr_and_assoc_item()
893
            lineNo.EvaluatedEQ()
894

    
895
        ''' set stream no '''
896
        if update_stream_no:
897
            set_stream_no(worker)
898

    
899
        ''' visible on '''
900
        ALL_ITEM = [item for item in worker.scene.items() if issubclass(type(item), QEngineeringAbstractItem)]
901
        for item in ALL_ITEM:
902
            item.setVisible(True)
903

    
904
    except Exception as ex:
905
        message = 'error occurred({}) in {}:{}'.format(repr(ex), sys.exc_info()[-1].tb_frame.f_code.co_filename,
906
                                                       sys.exc_info()[-1].tb_lineno)
907
        worker.display_message.emit(message)
908
    except:
909
        (type1, value, traceback) = sys.exc_info()
910
        sys.excepthook(type1, value, traceback)
911

    
912
def set_stream_no(worker):
913
    ''' set stream no '''
914

    
915
    from App import App
916
    from SetStreamNoCommand import SetStreamCommand
917
    from LineListDialog import LineListDialog
918

    
919
    cmd = SetStreamCommand()
920
    cmd.display_message.connect(App.mainWnd().onAddMessage)
921
    cmd.execute(worker.scene)
922

    
923
    dialog = LineListDialog(None, scene=worker.scene)
924
    AppDocData.instance()._streamLineListModelDatas = dialog._model_datas
925

    
926
def make_end_break(worker, end_breaks, lineNos):
927
    ''' make end break '''
928

    
929
    from App import App
930
    import os
931

    
932
    try:
933
        docdata = AppDocData.instance()
934

    
935
        end_break_names = docdata.getSymbolListByType('type', 'End Break')
936
        if len(end_break_names) is not 0:
937
            svgFileName = end_break_names[0].sName
938
            symbol = end_break_names[0]
939
            svgFilePath = os.path.join(docdata.getCurrentProject().getSvgFilePath(), symbol.getType(),
940
                                       svgFileName + '.svg')
941

    
942
            lineNo_froms = []
943
            lineNo_tos = []
944

    
945
            for lineNo in lineNos:
946
                # lineNo_froms.append(lineNo.prop('From')) if lineNo.prop('From') is not None else None
947
                # lineNo_tos.append(lineNo.prop('To')) if lineNo.prop('To') is not None else None
948

    
949
                for run in lineNo.runs:
950
                    start = [run.items[0]]
951
                    end = [run.items[-1]] if run.items[0] is not run.items[-1] else []
952
                    lineNo_froms.extend(start)
953
                    lineNo_tos.extend(end)
954

    
955
            for line_end in lineNo_froms + lineNo_tos:
956
                # print(type(line_end))
957
                for connector in line_end.connectors:
958
                    if connector.connectedItem is not None and type(
959
                            connector.connectedItem.owner) is QEngineeringLineNoTextItem and connector.connectedItem.owner is not line_end.owner:
960
                        end_break = SymbolSvgItem.createItem(symbol.getType(), None, svgFilePath)
961
                        pt = [connector.center()[0] - float(symbol.getOriginalPoint().split(',')[0]),
962
                              connector.center()[1] - float(symbol.getOriginalPoint().split(',')[1])]
963
                        origin = [0, 0]
964
                        if 2 == len(symbol.getOriginalPoint().split(',')):
965
                            tokens = symbol.getOriginalPoint().split(',')
966
                            origin = [pt[0] + float(tokens[0]), pt[1] + float(tokens[1])]
967
                        end_break.buildItem(svgFileName, symbol.getType(), 3.83, pt,
968
                                            [end_break.boundingRect().width(), end_break.boundingRect().height()],
969
                                            origin, [], symbol.getBaseSymbol(), symbol.getAdditionalSymbol(),
970
                                            symbol.getHasInstrumentLabel())
971

    
972
                        end_break.set_property('Connected Item', connector.connectedItem)
973
                        end_break.setToolTip('owner : ' + str(line_end))
974
                        end_break.area = 'Drawing'
975
                        end_break.owner = line_end
976
                        end_breaks.append(end_break)
977

    
978
            if end_breaks:
979
                # check duplication
980
                dupl = set()
981
                for i in range(len(end_breaks)):
982
                    for j in range(len(end_breaks)):
983
                        if i == j:
984
                            continue
985
                        else:
986
                            setI = set([end_breaks[i].owner, end_breaks[i].prop('Connected Item')])
987
                            setJ = set([end_breaks[j].owner, end_breaks[j].prop('Connected Item')])
988
                            if not (setI - setJ):
989
                                index = [i, j]
990
                                index.sort()
991
                                index = tuple(index)
992
                                dupl.add(index)
993
                # print(dupl)
994
                dupl = list(set(
995
                    [(indexSet[1] if not end_breaks[indexSet[1]].prop('Freeze') else indexSet[0]) for indexSet in
996
                     list(dupl)]))
997
                dupl.sort(reverse=True)
998
                # print(dupl)
999
                for index in dupl:
1000
                    end_breaks.pop(index)
1001

    
1002
                ''' test code for converter
1003
                for end_break in end_breaks:
1004
                    if type(end_break.prop('Connected Item')) is not QEngineeringLineItem and type(end_break.owner) is QEngineeringLineItem:
1005
                        connLine = end_break.prop('Connected Item')
1006
                        end_break.remove_assoc_item(connLine)
1007
                        end_break.set_property('Connected Item', end_break.owner)
1008
                        end_break.owner = connLine
1009
                '''
1010

    
1011
                for end_break in end_breaks:
1012
                    if not end_break.prop('Freeze'):
1013
                        end_break.transfer.onRemoved.connect(App.mainWnd().itemRemoved)
1014
                        # end break can be modeled only piping line
1015
                        if (type(end_break.owner) is not QEngineeringLineItem or (
1016
                                type(end_break.owner) is QEngineeringLineItem and (
1017
                                end_break.owner.lineType == 'Secondary' or end_break.owner.lineType == 'Primary'))) \
1018
                                and (type(end_break.prop('Connected Item')) is not QEngineeringLineItem or (
1019
                                type(end_break.prop('Connected Item')) is QEngineeringLineItem and (
1020
                                end_break.prop('Connected Item').lineType == 'Secondary' or end_break.prop(
1021
                            'Connected Item').lineType == 'Primary'))):
1022
                            end_break.addSvgItemToScene(worker.scene)
1023

    
1024
    except Exception as ex:
1025
        message = 'error occurred({}) in {}:{}'.format(repr(ex), sys.exc_info()[-1].tb_frame.f_code.co_filename,
1026
                                                       sys.exc_info()[-1].tb_lineno)
1027
        worker.display_message.emit(message)
1028

    
1029

    
1030
def make_spec(worker, spec_breaks, lines, lineNos):
1031
    ''' update spec break '''
1032

    
1033
    from App import App
1034
    import os, math
1035

    
1036
    try:
1037
        docdata = AppDocData.instance()
1038

    
1039
        configs = docdata.getConfigs('Symbol', 'Spec')
1040
        spec_none = int(configs[0].value) if configs else -1
1041

    
1042
        freezed_spec_breaks = []
1043
        for spec_break in spec_breaks:
1044
            if not spec_break.prop('Freeze'):
1045
                spec_break.transfer.onRemoved.emit(spec_break)
1046
            else:
1047
                freezed_spec_breaks.append(spec_break)
1048

    
1049
        for line in lines:
1050
            line.clear_labels()
1051

    
1052
        spec_break_names = docdata.getSymbolListByType('type', 'Segment Breaks')
1053
        if len(spec_break_names) is not 0:
1054
            configs = docdata.getConfigs('Range', 'Detection Ratio')
1055
            ratio = float(configs[0].value) if 1 == len(configs) else 1.5
1056
            ratio *= 2
1057

    
1058
            svgFileName = spec_break_names[0].sName
1059
            symbol = spec_break_names[0]
1060
            svgFilePath = os.path.join(docdata.getCurrentProject().getSvgFilePath(), symbol.getType(),
1061
                                       svgFileName + '.svg')
1062

    
1063
            specBreakAttrsFull = [attr for attr in docdata.getSymbolAttribute('Segment Breaks') if
1064
                                  attr.Target == 'ALL' and (
1065
                                          attr.AttributeType == 'Spec' or attr.AttributeType == 'String')]
1066
            specBreakAttrs = [attr.Attribute for attr in specBreakAttrsFull]
1067

    
1068
            line_ends = []
1069
            # append upstream first and append downstream
1070
            for lineNo in lineNos:
1071
                for run in lineNo.runs:
1072
                    line_ends.append(run.items[0]) if issubclass(type(run.items[0]), SymbolSvgItem) or (
1073
                            type(run.items[0]) is QEngineeringLineItem and (
1074
                            run.items[0].lineType == 'Secondary' or run.items[
1075
                        0].lineType == 'Primary')) else None
1076
            for lineNo in lineNos:
1077
                for run in lineNo.runs:
1078
                    if run.items[0] is not run.items[-1]:
1079
                        line_ends.append(run.items[-1]) if issubclass(type(run.items[-1]), SymbolSvgItem) or (
1080
                                type(run.items[-1]) is QEngineeringLineItem and (
1081
                                run.items[-1].lineType == 'Secondary' or run.items[
1082
                            -1].lineType == 'Primary')) else None
1083

    
1084
            spec_breaks = []
1085
            for line_end in line_ends:
1086
                for connector in line_end.connectors:
1087
                    if connector.connectedItem is not None and type(
1088
                            connector.connectedItem.owner) is QEngineeringLineNoTextItem and connector.connectedItem.owner is not line_end.owner:
1089
                        spec_break = []  # upstream, downstream, [spec, up value, down value], ... , [ ... ]
1090
                        match = False
1091
                        for prop, value in [[prop, value] for prop, value in line_end.owner.getAttributes().items()
1092
                                            if prop.Attribute in specBreakAttrs]:
1093
                            target = [[prop2, value2] for prop2, value2 in connector.connectedItem.owner.getAttributes().items() if
1094
                                                  prop2.Attribute in specBreakAttrs and prop2.UID == prop.UID]
1095
                            if target:
1096
                                for prop2, value2 in target:
1097
                                    if str(prop.UID) == str(prop2.UID) and value != value2:
1098
                                        if not match:
1099
                                            spec_break.extend([line_end, connector.connectedItem])
1100
                                            match = True
1101
                                        spec_break.append([prop.Attribute, value, value2])
1102
                            elif spec_none == 1:
1103
                                # not pair
1104
                                if not match:
1105
                                    spec_break.extend([line_end, connector.connectedItem])
1106
                                    match = True
1107
                                spec_break.append([prop.Attribute, value, ''])
1108

    
1109
                        # check in reverse order for not pair
1110
                        if spec_none == 1:
1111
                            for prop, value in [[prop, value] for prop, value in connector.connectedItem.owner.getAttributes().items()
1112
                                                if prop.Attribute in specBreakAttrs]:
1113
                                target = [[prop2, value2] for prop2, value2 in line_end.owner.getAttributes().items() if
1114
                                                    prop2.Attribute in specBreakAttrs and prop2.UID == prop.UID]
1115
                                if target:
1116
                                    pass
1117
                                    '''
1118
                                    for prop2, value2 in target:
1119
                                        if str(prop.UID) == str(prop2.UID) and value != value2:
1120
                                            if not match:
1121
                                                spec_break.extend([line_end, connector.connectedItem])
1122
                                                match = True
1123
                                                spec_break.append([prop.Attribute, value2, value])
1124
                                            elif prop.Attribute not in [_attr[0] for _attr in spec_break[2:]:
1125
                                                spec_break.append([prop.Attribute, value2, value])
1126
                                    '''
1127
                                else:
1128
                                    # not pair
1129
                                    if not match:
1130
                                        spec_break.extend([line_end, connector.connectedItem])
1131
                                        match = True
1132
                                    spec_break.append([prop.Attribute, '', value])
1133
                        if match:
1134
                            spec_breaks.append(spec_break)
1135

    
1136
            if spec_breaks:
1137
                # check duplication
1138
                dupl = set()
1139
                for i in range(len(spec_breaks)):
1140
                    for j in range(len(spec_breaks)):
1141
                        if i == j:
1142
                            continue
1143
                        else:
1144
                            setI = set([spec_breaks[i][0], spec_breaks[i][1]])
1145
                            setJ = set([spec_breaks[j][0], spec_breaks[j][1]])
1146
                            if not (setI - setJ):
1147
                                if len(spec_breaks[i]) == len(spec_breaks[j]):
1148
                                    index = [i, j]
1149
                                    index.sort()
1150
                                else:
1151
                                    index = [i, j] if len(spec_breaks[i]) > len(spec_breaks[j]) else [j, i]
1152
                                index = tuple(index)
1153
                                dupl.add(index)
1154
                dupl = list(set([(indexSet[1]) for indexSet in list(dupl)]))
1155
                dupl.sort(reverse=True)
1156
                for index in dupl:
1157
                    spec_breaks.pop(index)
1158
                # up to here
1159

    
1160
                spec_break_items = []
1161
                for spec in spec_breaks:
1162
                    dupl = False
1163
                    for freezed in freezed_spec_breaks:
1164
                        freezed_attrs = freezed.getAttributes()
1165
                        up = [attr.AssocItem for attr in freezed_attrs if attr.Attribute == 'UpStream']
1166
                        down = [attr.AssocItem for attr in freezed_attrs if attr.Attribute == 'DownStream']
1167
                        if up and down:
1168
                            if (up[0] is spec[0] and down[0] is spec[1]) or (
1169
                                    up[0] is spec[1] and down[0] is spec[0]):
1170
                                dupl = True
1171
                                break
1172
                    if dupl:
1173
                        continue
1174

    
1175
                    for connector in spec[0].connectors:
1176
                        if connector.connectedItem is spec[1]:
1177
                            spec_break = SymbolSvgItem.createItem(symbol.getType(), None, svgFilePath)
1178
                            pt = [40 + connector.center()[0] - float(symbol.getOriginalPoint().split(',')[0]),
1179
                                  60 + connector.center()[1] - float(symbol.getOriginalPoint().split(',')[1])]
1180
                            origin = [0, 0]
1181
                            if 2 == len(symbol.getOriginalPoint().split(',')):
1182
                                tokens = symbol.getOriginalPoint().split(',')
1183
                                origin = [pt[0] + float(tokens[0]), pt[1] + float(tokens[1])]
1184
                            spec_break.buildItem(svgFileName, symbol.getType(), 3.14, pt,
1185
                                                 [spec_break.boundingRect().width(),
1186
                                                  spec_break.boundingRect().height()], origin, [],
1187
                                                 symbol.getBaseSymbol(), symbol.getAdditionalSymbol(),
1188
                                                 symbol.getHasInstrumentLabel())
1189

    
1190
                            attrs = spec_break.getAttributes()
1191
                            for key in attrs.keys():
1192
                                if key.Attribute == 'UpStream':
1193
                                    attrs[key] = str(spec[0])
1194
                                    spec_break.add_assoc_item(spec[0], key.AttrAt, force=True)
1195
                                    key.AssocItem = spec[0]
1196
                                elif key.Attribute == 'DownStream':
1197
                                    attrs[key] = str(spec[1])
1198
                                    spec_break.add_assoc_item(spec[1], key.AttrAt, force=True)
1199
                                    key.AssocItem = spec[1]
1200

    
1201
                            for attr, value, value2 in spec[2:]:
1202
                                for full in specBreakAttrsFull:
1203
                                    if full.Attribute == attr:
1204
                                        attrs[full] = [value, value2]
1205

    
1206
                            # find label text for spec break line
1207
                            stream_line = [spec[0], spec[1]]
1208
                            stream_track = [spec[1], spec[0]]
1209
                            stream_res = [False, False]
1210
                            for index in range(len(stream_line)):
1211
                                while True:
1212
                                    if type(stream_line[index]) is QEngineeringLineItem:
1213
                                        stream_res[index] = True
1214
                                        break
1215
                                    else:
1216
                                        find_next = False
1217
                                        connected_count = 0
1218
                                        for connectorr in stream_line[index].connectors:
1219
                                            connected_count += 1
1220
                                            if connectorr.connectedItem and stream_track[index] is not connectorr.connectedItem and \
1221
                                                    stream_line[index].next_connected(stream_track[index], connectorr.connectedItem):
1222
                                                stream_track[index] = stream_line[index]
1223
                                                stream_line[index] = connectorr.connectedItem
1224
                                                find_next = True
1225
                                                break
1226

    
1227
                                        if not find_next:
1228
                                            # prevent infinite loop
1229
                                            if connected_count == 2:
1230
                                                for connectorr in stream_line[index].connectors:
1231
                                                    if connectorr.connectedItem and not connectorr.connectedItem is stream_track[index]:
1232
                                                        stream_line[index] = connectorr.connectedItem
1233
                                                        stream_track[index] = stream_line[index]
1234
                                                        find_next = True
1235
                                                        break
1236
                                                if not find_next:
1237
                                                    break
1238
                                            else:
1239
                                                break
1240

    
1241
                            if stream_res[0] and stream_res[1]:
1242
                                texts = [item for item in worker.scene.items() if
1243
                                         type(item) is QEngineeringTextItem and item.owner is None]
1244
                                positioning = False
1245

    
1246
                                for attr, value, value2 in spec[2:]:
1247
                                    up_texts = [text for text in texts if text.text() == value]
1248
                                    down_texts = [text for text in texts if text.text() == value2]
1249
                                    up_down_texts = [up_texts, down_texts]
1250
                                    up_down_find = [None, None]
1251

    
1252
                                    for index in range(len(up_down_texts)):
1253
                                        minDist = sys.maxsize
1254

    
1255
                                        for up_down_text in up_down_texts[index]:
1256
                                            dx = connector.center()[0] - up_down_text.center().x()
1257
                                            dy = connector.center()[1] - up_down_text.center().y()
1258
                                            dist = (up_down_text.sceneBoundingRect().height() + up_down_text.sceneBoundingRect().width()) * ratio / 2
1259
                                            length = math.sqrt(dx * dx + dy * dy)
1260
                                            if length < dist and length < minDist:
1261
                                                up_down_find[index] = up_down_text
1262

    
1263
                                    if up_down_find[0] and up_down_find[1]:
1264
                                        for index in range(len(stream_line)):
1265
                                            attrs = stream_line[index].getAttributes()
1266
                                            for key in attrs.keys():
1267
                                                if key.Attribute == attr:
1268
                                                    attrs[key] = up_down_find[index].text()
1269
                                                    key.AssocItem = up_down_find[index]
1270
                                                    stream_line[index].add_assoc_item(up_down_find[index],
1271
                                                                                      key.AttrAt, force=True)
1272
                                                    up_down_find[index].owner = stream_line[index]
1273
                                                    break
1274

    
1275
                                        if not positioning:
1276
                                            # set spec break position between
1277
                                            positioning = True
1278
                                            new_x = round(
1279
                                                (up_down_find[0].center().x() + up_down_find[1].center().x()) / 2)
1280
                                            new_y = round(
1281
                                                (up_down_find[0].center().y() + up_down_find[1].center().y()) / 2)
1282
                                            spec_break.loc = [new_x - spec_break.symbolOrigin[0],
1283
                                                              new_y - spec_break.symbolOrigin[1]]
1284
                                            spec_break.origin = [new_x, new_y]
1285

    
1286
                                            pivot = None
1287
                                            for connector in spec[0].connectors:
1288
                                                if connector.connectedItem is spec[1]:
1289
                                                    pivot = connector.sceneBoundingRect().center()
1290
                                                    break
1291

    
1292
                                            if abs(up_down_find[0].center().x() - up_down_find[
1293
                                                1].center().x()) < abs(
1294
                                                up_down_find[0].center().y() - up_down_find[1].center().y()):
1295
                                                if new_x > pivot.x():
1296
                                                    spec_break.angle = 1.57
1297
                                                else:
1298
                                                    spec_break.angle = 4.71239
1299
                                            else:
1300
                                                if new_y > pivot.y():
1301
                                                    spec_break.angle = 3.14
1302
                                                else:
1303
                                                    spec_break.angle = 0
1304

    
1305
                                            # make show prop true
1306
                                            spec_break.set_property('Show', True)
1307

    
1308
                            spec_break_items.append(spec_break)
1309

    
1310
                for spec_break_item in spec_break_items:
1311
                    spec_break_item.transfer.onRemoved.connect(App.mainWnd().itemRemoved)
1312
                    spec_break_item.addSvgItemToScene(worker.scene)
1313

    
1314
                spec_break_items.extend(freezed_spec_breaks)
1315
    except Exception as ex:
1316
        message = 'error occurred({}) in {}:{}'.format(repr(ex), sys.exc_info()[-1].tb_frame.f_code.co_filename,
1317
                                                       sys.exc_info()[-1].tb_lineno)
1318
        worker.display_message.emit(message)
1319

    
1320

    
1321
def make_flow_mark(worker, lines):
1322
    ''' make flow mark '''
1323

    
1324
    try:
1325
        docdata = AppDocData.instance()
1326

    
1327
        for line in lines:
1328
            line.flowMark = None
1329
            # line.update_arrow()
1330

    
1331
        configs = docdata.getConfigs('Flow Mark', 'Position')
1332
        position = int(configs[0].value) if 1 == len(configs) else 100
1333
        configs = docdata.getConfigs('Flow Mark', 'Length')
1334
        length = int(configs[0].value) if 1 == len(configs) else 200
1335

    
1336
        for line in [line for line in lines if line.is_piping(True)]:
1337
            line.update_flow_mark(position, length)
1338
            line.update_arrow()
1339
        # for lineNo in docdata.tracerLineNos:
1340
        #    lineNo.update_flow_mark(position, length)
1341

    
1342
    except Exception as ex:
1343
        message = 'error occurred({}) in {}:{}'.format(repr(ex), sys.exc_info()[-1].tb_frame.f_code.co_filename,
1344
                                                       sys.exc_info()[-1].tb_lineno)
1345
        worker.display_message.emit(message)
1346

    
1347

    
1348
def sort_run_flow(worker):
1349
    ''' sort runs '''
1350

    
1351
    from EngineeringLineTracerRunItem import QEngineeringLineTracerRunItem
1352

    
1353
    try:
1354
        docdata = AppDocData.instance()
1355

    
1356
        fixed_run_infos = []  # QEngineeringLineTracerRunItem s
1357
        waiting_run_infos = []  # QEngineeringLineTracerRunItem s
1358
        runs = []
1359
        # first step : make fixed run using symbol info
1360
        for lineNo in [lineNo for lineNo in docdata.tracerLineNos if len(lineNo.runs) > 0]:
1361
            not_trim = True if type(lineNo) is QEngineeringLineNoTextItem else False
1362
            run_index = 0
1363
            for run in lineNo.runs:
1364
                not_secondary = True if run_index is 0 else False
1365

    
1366
                # if from and to info already was entered, fix run flow
1367
                if hasattr(lineNo, '_fixed') and lineNo._fixed and not_secondary:
1368
                    fixed_run_infos.append(QEngineeringLineTracerRunItem(run, lineNo, not_trim, not_secondary))
1369
                    run_index += 1
1370
                    continue
1371
                elif hasattr(run, '_fixed') and run._fixed:
1372
                    fixed_run_infos.append(QEngineeringLineTracerRunItem(run, lineNo, not_trim, not_secondary))
1373
                    run_index += 1
1374
                    continue
1375

    
1376
                reference_symbols = [item for item in run.items if
1377
                                     issubclass(type(item), SymbolSvgItem) and item.has_in_out_connector()]
1378
                # runs can know flow directly
1379
                if len(run.items) > 1 and len(reference_symbols) > 0:
1380
                    for reference_symbol in reference_symbols:
1381
                        # place at first
1382
                        if reference_symbol is run.items[0]:
1383
                            if len([connector_index for connector_index in reference_symbol.in_out_connector[0] \
1384
                                    if reference_symbol.connectors[connector_index].connectedItem is run.items[1]]) > 0:
1385
                                info = QEngineeringLineTracerRunItem(run, lineNo, not_trim, not_secondary)
1386
                                info.reverse()
1387
                                fixed_run_infos.append(info)
1388
                                break
1389
                            elif len([connector_index for connector_index in reference_symbol.in_out_connector[1] \
1390
                                      if reference_symbol.connectors[connector_index].connectedItem is run.items[1]]) > 0:
1391
                                info = QEngineeringLineTracerRunItem(run, lineNo, not_trim, not_secondary)
1392
                                fixed_run_infos.append(info)
1393
                                break
1394
                        # place at last
1395
                        elif reference_symbol is run.items[-1]:
1396
                            if len([connector_index for connector_index in reference_symbol.in_out_connector[1] \
1397
                                    if
1398
                                    reference_symbol.connectors[connector_index].connectedItem is run.items[-2]]) > 0:
1399
                                info = QEngineeringLineTracerRunItem(run, lineNo, not_trim, not_secondary)
1400
                                info.reverse()
1401
                                fixed_run_infos.append(info)
1402
                                break
1403
                            elif len([connector_index for connector_index in reference_symbol.in_out_connector[0] \
1404
                                      if
1405
                                      reference_symbol.connectors[connector_index].connectedItem is run.items[-2]]) > 0:
1406
                                info = QEngineeringLineTracerRunItem(run, lineNo, not_trim, not_secondary)
1407
                                fixed_run_infos.append(info)
1408
                                break
1409
                        # place at middle
1410
                        else:
1411
                            in_items = [reference_symbol.connectors[connector_index].connectedItem for connector_index
1412
                                        in reference_symbol.in_out_connector[0] \
1413
                                        if reference_symbol.connectors[connector_index].connectedItem in run.items]
1414
                            out_items = [reference_symbol.connectors[connector_index].connectedItem for connector_index
1415
                                         in reference_symbol.in_out_connector[1] \
1416
                                         if reference_symbol.connectors[connector_index].connectedItem in run.items]
1417
                            in_item = in_items[0] if in_items else None
1418
                            out_item = out_items[0] if out_items else None
1419
                            if in_item and out_item:
1420
                                in_item_index = run.items.index(in_item)
1421
                                out_item_index = run.items.index(out_item)
1422
                                if out_item_index < in_item_index:
1423
                                    info = QEngineeringLineTracerRunItem(run, lineNo, not_trim, not_secondary)
1424
                                    info.reverse()
1425
                                    fixed_run_infos.append(info)
1426
                                    break
1427
                                else:
1428
                                    info = QEngineeringLineTracerRunItem(run, lineNo, not_trim, not_secondary)
1429
                                    fixed_run_infos.append(info)
1430
                                    break
1431
                # only symbol runs doesn't need flow
1432
                elif len(run.items) is 1 and issubclass(type(run.items[0]), SymbolSvgItem):
1433
                    runs.append(QEngineeringLineTracerRunItem(run, lineNo, not_trim, not_secondary))
1434
                # runs can't know flow directly
1435
                else:
1436
                    waiting_run_infos.append(QEngineeringLineTracerRunItem(run, lineNo, not_trim, not_secondary))
1437

    
1438
                run_index += 1
1439

    
1440
        QApplication.processEvents()
1441
        # second step : determine waiting run flow, connected by point
1442
        remain_count_past = len(waiting_run_infos)
1443
        while True:
1444
            remain_count = 0
1445
            for run_index in reversed(range(len(waiting_run_infos))):
1446
                waiting_run_info = waiting_run_infos[run_index]
1447
                waiting_run = waiting_run_info.run
1448

    
1449
                find = False
1450
                for fixed_run_info in fixed_run_infos:
1451
                    fixed_run = fixed_run_info.run
1452
                    if len(waiting_run.items) > 1 and len(fixed_run.items) > 1:
1453
                        # waiting_run and fix_run have items more than 2
1454
                        if waiting_run.items[0].is_connected(fixed_run.items[0]):
1455
                            waiting_run_info.reverse()
1456
                            fixed_run_infos.append(waiting_run_info)
1457
                            waiting_run_infos.pop(run_index)
1458
                            find = True
1459
                            break
1460
                        elif waiting_run.items[0].is_connected(fixed_run.items[-1]):
1461
                            fixed_run_infos.append(waiting_run_info)
1462
                            waiting_run_infos.pop(run_index)
1463
                            find = True
1464
                            break
1465
                        elif waiting_run.items[-1].is_connected(fixed_run.items[-1]):
1466
                            waiting_run_info.reverse()
1467
                            fixed_run_infos.append(waiting_run_info)
1468
                            waiting_run_infos.pop(run_index)
1469
                            find = True
1470
                            break
1471
                        elif waiting_run.items[-1].is_connected(fixed_run.items[0]):
1472
                            fixed_run_infos.append(waiting_run_info)
1473
                            waiting_run_infos.pop(run_index)
1474
                            find = True
1475
                            break
1476
                    elif len(fixed_run.items) > 1:
1477
                        # waiting_run is single line
1478
                        if waiting_run.items[0].connectors[0].connectedItem is fixed_run.items[0]:
1479
                            waiting_run_info.reverse()
1480
                            fixed_run_infos.append(waiting_run_info)
1481
                            waiting_run_infos.pop(run_index)
1482
                            find = True
1483
                            break
1484
                        elif waiting_run.items[0].connectors[0].connectedItem is fixed_run.items[-1]:
1485
                            fixed_run_infos.append(waiting_run_info)
1486
                            waiting_run_infos.pop(run_index)
1487
                            find = True
1488
                            break
1489
                        elif waiting_run.items[0].connectors[1].connectedItem is fixed_run.items[-1]:
1490
                            waiting_run_info.reverse()
1491
                            fixed_run_infos.append(waiting_run_info)
1492
                            waiting_run_infos.pop(run_index)
1493
                            find = True
1494
                            break
1495
                        elif waiting_run.items[0].connectors[1].connectedItem is fixed_run.items[0]:
1496
                            fixed_run_infos.append(waiting_run_info)
1497
                            waiting_run_infos.pop(run_index)
1498
                            find = True
1499
                            break
1500
                    else:
1501
                        if waiting_run.items[0].is_connected(fixed_run.items[0]) and fixed_run.items[0].connectors[
1502
                            0].connectedItem is waiting_run.items[0]:
1503
                            waiting_run.reverse()
1504
                            fixed_run_infos.append(waiting_run_info)
1505
                            waiting_run_infos.pop(run_index)
1506
                            find = True
1507
                            break
1508
                        elif waiting_run.items[0].is_connected(fixed_run.items[0]) and fixed_run.items[0].connectors[
1509
                            1].connectedItem is waiting_run.items[0]:
1510
                            fixed_run_infos.append(waiting_run_info)
1511
                            waiting_run_infos.pop(run_index)
1512
                            find = True
1513
                            break
1514
                        elif waiting_run.items[0].is_connected(fixed_run.items[0]) and fixed_run.items[0].connectors[
1515
                            1].connectedItem is waiting_run.items[0]:
1516
                            waiting_run.reverse()
1517
                            fixed_run_infos.append(waiting_run_info)
1518
                            waiting_run_infos.pop(run_index)
1519
                            find = True
1520
                            break
1521
                        elif waiting_run.items[0].is_connected(fixed_run.items[0]) and fixed_run.items[0].connectors[
1522
                            0].connectedItem is waiting_run.items[0]:
1523
                            fixed_run_infos.append(waiting_run_info)
1524
                            waiting_run_infos.pop(run_index)
1525
                            find = True
1526
                            break
1527
                if not find:
1528
                    remain_count += 1
1529

    
1530
            if remain_count_past == remain_count:
1531
                break
1532
            else:
1533
                remain_count_past = remain_count
1534

    
1535
        QApplication.processEvents()
1536
        # third step : body connected run sort, not split
1537
        remain_count_past = len(waiting_run_infos)
1538
        while True:
1539
            remain_count = 0
1540
            for run_index in reversed(range(len(waiting_run_infos))):
1541
                waiting_run_info = waiting_run_infos[run_index]
1542
                waiting_run = waiting_run_info.run
1543

    
1544
                find = False
1545
                for fixed_run_info in fixed_run_infos:
1546
                    fixed_run = fixed_run_info.run
1547
                    if len(waiting_run.items) > 1 and len(fixed_run.items) > 1 and type(
1548
                            waiting_run.items[0]) is QEngineeringLineItem and type(
1549
                        waiting_run.items[-1]) is QEngineeringLineItem:
1550
                        if waiting_run.items[0].connectors[0].connectedItem in fixed_run.items and \
1551
                                waiting_run.items[-1].connectors[1].connectedItem in fixed_run.items:
1552
                            if fixed_run.items.index(
1553
                                    waiting_run.items[0].connectors[0].connectedItem) > fixed_run.items.index(
1554
                                waiting_run.items[-1].connectors[1].connectedItem):
1555
                                waiting_run.reverse()
1556
                                fixed_run_infos.append(waiting_run_info)
1557
                                waiting_run_infos.pop(run_index)
1558
                                find = True
1559
                                break
1560
                            else:
1561
                                fixed_run_infos.append(waiting_run_info)
1562
                                waiting_run_infos.pop(run_index)
1563
                                find = True
1564
                                break
1565

    
1566
                if not find:
1567
                    remain_count += 1
1568

    
1569
            if remain_count_past == remain_count:
1570
                break
1571
            else:
1572
                remain_count_past = remain_count
1573

    
1574
        # fourth step : body connected split run or split owner run sort
1575
        fixed_merged_run_infos = []  # [[run_info1, run_info2, ... in order], [merged run with items]]
1576
        waiting_merged_run_infos = []  # [[run_info1, run_info2, ... in order], [merged run with items]]
1577

    
1578
        QApplication.processEvents()
1579
        # waiting run merge and sort
1580
        consumed_count_past = 0
1581
        for index1 in range(len(waiting_run_infos)):
1582
            if waiting_run_infos[index1].consumed:
1583
                continue
1584
            for index2 in range(len(waiting_run_infos)):
1585
                if index1 == index2 or waiting_run_infos[index2].consumed:
1586
                    continue
1587

    
1588
                result, merged_run_info, header, reverse = QEngineeringLineTracerRunItem.merge_and_sort_if_possible(
1589
                    waiting_run_infos[index1], waiting_run_infos[index2])
1590
                if result:
1591
                    waiting_run_infos[index1].consumed = True
1592
                    waiting_run_infos[index2].consumed = True
1593
                    consumed_count_past += 2
1594
                    if header:
1595
                        waiting_merged_run_infos.append(
1596
                            [[waiting_run_infos[index2], waiting_run_infos[index1]], merged_run_info])
1597
                    else:
1598
                        waiting_merged_run_infos.append(
1599
                            [[waiting_run_infos[index1], waiting_run_infos[index2]], merged_run_info])
1600
                    break
1601

    
1602
        QApplication.processEvents()
1603
        while True:
1604
            consumed_count = 0
1605
            for index in range(len(waiting_run_infos)):
1606
                if waiting_run_infos[index].consumed:
1607
                    consumed_count += 1
1608
                    continue
1609

    
1610
                for waiting_merged_run_info in waiting_merged_run_infos:
1611
                    result, merged_run_info, header, reverse = QEngineeringLineTracerRunItem.merge_and_sort_if_possible(
1612
                        waiting_merged_run_info[1], waiting_run_infos[index])
1613
                    if result:
1614
                        waiting_run_infos[index].consumed = True
1615
                        consumed_count += 1
1616
                        if header:
1617
                            waiting_merged_run_info[0].insert(0, waiting_run_infos[index])
1618
                            waiting_merged_run_info[1] = merged_run_info
1619
                        else:
1620
                            waiting_merged_run_info[0].append(waiting_run_infos[index])
1621
                            waiting_merged_run_info[1] = merged_run_info
1622
                        break
1623

    
1624
            if consumed_count_past == consumed_count:
1625
                break
1626
            else:
1627
                consumed_count_past = consumed_count
1628

    
1629
        QApplication.processEvents()
1630
        while True:
1631
            merged = False
1632
            for index1 in range(len(waiting_merged_run_infos)):
1633
                waiting_merged_run_info1 = waiting_merged_run_infos[index1]
1634
                for index2 in range(len(waiting_merged_run_infos)):
1635
                    if index1 == index2:
1636
                        continue
1637
                    waiting_merged_run_info2 = waiting_merged_run_infos[index2]
1638
                    result, merged_run_info, header, reverse = QEngineeringLineTracerRunItem.merge_and_sort_if_possible(
1639
                        waiting_merged_run_info1[1], waiting_merged_run_info2[1], sort_connect_info=False)
1640
                    if result:
1641
                        if header:
1642
                            if reverse:
1643
                                for waiting_run_info in waiting_merged_run_info2[0]:
1644
                                    waiting_run_info.reverse()
1645
                                waiting_merged_run_info2[0].reverse()
1646
                                waiting_merged_run_info2[0].extend(waiting_merged_run_info1[0])
1647
                            waiting_merged_run_info1[0] = waiting_merged_run_info2[0]
1648
                            waiting_merged_run_info1[1] = merged_run_info
1649
                        else:
1650
                            if reverse:
1651
                                for waiting_run_info in waiting_merged_run_info2[0]:
1652
                                    waiting_run_info.reverse()
1653
                                waiting_merged_run_info2[0].reverse()
1654
                            waiting_merged_run_info1[0].extend(waiting_merged_run_info2[0])
1655
                            waiting_merged_run_info1[1] = merged_run_info
1656
                        merged = True
1657
                        break
1658
                if merged:
1659
                    waiting_merged_run_infos.pop(index2)
1660
                    break
1661
            if not merged:
1662
                break
1663

    
1664
        QApplication.processEvents()
1665
        # fixed run merge
1666
        consumed_count_past = 0
1667
        for index1 in range(len(fixed_run_infos)):
1668
            if fixed_run_infos[index1].consumed:
1669
                continue
1670
            for index2 in range(len(fixed_run_infos)):
1671
                if index1 == index2 or fixed_run_infos[index2].consumed:
1672
                    continue
1673

    
1674
                result, merged_run_info, header, reverse = QEngineeringLineTracerRunItem.merge_and_sort_if_possible(
1675
                    fixed_run_infos[index1], fixed_run_infos[index2], sort=False)
1676
                if result:
1677
                    fixed_run_infos[index1].consumed = True
1678
                    fixed_run_infos[index2].consumed = True
1679
                    consumed_count_past += 2
1680
                    if header:
1681
                        fixed_merged_run_infos.append(
1682
                            [[fixed_run_infos[index2], fixed_run_infos[index1]], merged_run_info])
1683
                    else:
1684
                        fixed_merged_run_infos.append(
1685
                            [[fixed_run_infos[index1], fixed_run_infos[index2]], merged_run_info])
1686
                    break
1687

    
1688
        QApplication.processEvents()
1689
        while True:
1690
            consumed_count = 0
1691
            for index in range(len(fixed_run_infos)):
1692
                if fixed_run_infos[index].consumed:
1693
                    consumed_count += 1
1694
                    continue
1695

    
1696
                for fixed_merged_run_info in fixed_merged_run_infos:
1697
                    result, merged_run_info, header, reverse = QEngineeringLineTracerRunItem.merge_and_sort_if_possible(
1698
                        fixed_merged_run_info[1], fixed_run_infos[index], sort=False)
1699
                    if result:
1700
                        fixed_run_infos[index].consumed = True
1701
                        consumed_count += 1
1702
                        if header:
1703
                            fixed_merged_run_info[0].insert(0, fixed_run_infos[index])
1704
                            fixed_merged_run_info[1] = merged_run_info
1705
                        else:
1706
                            fixed_merged_run_info[0].append(fixed_run_infos[index])
1707
                            fixed_merged_run_info[1] = merged_run_info
1708
                        break
1709

    
1710
            if consumed_count_past == consumed_count:
1711
                break
1712
            else:
1713
                consumed_count_past = consumed_count
1714

    
1715
        QApplication.processEvents()
1716
        while True:
1717
            merged = False
1718
            for index1 in range(len(fixed_merged_run_infos)):
1719
                fixed_merged_run_info1 = fixed_merged_run_infos[index1]
1720
                for index2 in range(len(fixed_merged_run_infos)):
1721
                    if index1 == index2:
1722
                        continue
1723
                    fixed_merged_run_info2 = fixed_merged_run_infos[index2]
1724
                    result, merged_run_info, header, reverse = QEngineeringLineTracerRunItem.merge_and_sort_if_possible(
1725
                        fixed_merged_run_info1[1], fixed_merged_run_info2[1], sort=False)
1726
                    if result:
1727
                        if header:
1728
                            fixed_merged_run_info1[0] = fixed_merged_run_info2[0]
1729
                            fixed_merged_run_info1[1] = merged_run_info
1730
                        else:
1731
                            fixed_merged_run_info1[0].extend(fixed_merged_run_info2[0])
1732
                            fixed_merged_run_info1[1] = merged_run_info
1733
                        merged = True
1734
                        break
1735
                if merged:
1736
                    fixed_merged_run_infos.pop(index2)
1737
                    break
1738
            if not merged:
1739
                break
1740

    
1741
        for fixed_run_info in fixed_run_infos:
1742
            if fixed_run_info.consumed:
1743
                continue
1744
            else:
1745
                fixed_merged_run_infos.append([[fixed_run_info], fixed_run_info])
1746

    
1747
        QApplication.processEvents()
1748
        # sort merged waiting runs by using merged fixed runs
1749
        remain_count_past = len(waiting_merged_run_infos)
1750
        while True:
1751
            remain_count = 0
1752
            for run_index in reversed(range(len(waiting_merged_run_infos))):
1753
                waiting_run_info = waiting_merged_run_infos[run_index][0]
1754
                waiting_merged_run = waiting_merged_run_infos[run_index][1].run
1755

    
1756
                find = False
1757
                for fixed_merged_run_info in fixed_merged_run_infos:
1758
                    fixed_merged_run = fixed_merged_run_info[1].run
1759
                    if len(waiting_merged_run.items) > 1 and len(fixed_merged_run.items) > 1 and type(
1760
                            waiting_merged_run.items[0]) is QEngineeringLineItem and type(
1761
                        waiting_merged_run.items[-1]) is QEngineeringLineItem:
1762
                        if waiting_merged_run.items[0].connectors[0].connectedItem in fixed_merged_run.items and \
1763
                                waiting_merged_run.items[-1].connectors[1].connectedItem in fixed_merged_run.items:
1764
                            if fixed_merged_run.items.index(waiting_merged_run.items[0].connectors[
1765
                                                                0].connectedItem) > fixed_merged_run.items.index(
1766
                                waiting_merged_run.items[-1].connectors[1].connectedItem):
1767
                                for waiting_run_info in waiting_merged_run_infos[run_index][0]:
1768
                                    waiting_run_info.reverse()
1769
                                waiting_merged_run_infos[run_index][0].reverse()
1770
                                reverse_order = []
1771
                                for index in reversed(range(len(waiting_merged_run_infos[run_index][1].run.items))):
1772
                                    reverse_order.append(waiting_merged_run_infos[run_index][1].run.items[index])
1773
                                waiting_merged_run_infos[run_index][1].run.items = reverse_order
1774
                                fixed_merged_run_infos.append(waiting_merged_run_infos[run_index])
1775
                                waiting_merged_run_infos.pop(run_index)
1776
                                find = True
1777
                                break
1778
                            else:
1779
                                fixed_merged_run_infos.append(waiting_merged_run_infos[run_index])
1780
                                waiting_merged_run_infos.pop(run_index)
1781
                                find = True
1782
                                break
1783

    
1784
                if not find:
1785
                    remain_count += 1
1786

    
1787
            if remain_count_past == remain_count:
1788
                break
1789
            else:
1790
                remain_count_past = remain_count
1791

    
1792
        # sort Eq drain
1793
        for waiting_run_info in [waiting_run_info for waiting_run_info in waiting_run_infos if waiting_run_info.consumed == False and waiting_run_info.not_trim == False]:
1794
            if len(waiting_run_info.run.items) > 1 and type(waiting_run_info.run.items[-1]) is QEngineeringNozzleItem:
1795
                if [item for item in waiting_run_info.run.items if type(item) is QEngineeringLineItem and item.is_piping(False)]:
1796
                    waiting_run_info.reverse()
1797

    
1798
    except Exception as ex:
1799
        message = 'error occurred({}) in {}:{}'.format(repr(ex), sys.exc_info()[-1].tb_frame.f_code.co_filename,
1800
                                                       sys.exc_info()[-1].tb_lineno)
1801
        worker.display_message.emit(message)
1802

    
1803
async def connectAttrImpl_inst(worker, update_line_type, update_flow_mark, update_spec, update_stream_no):
1804
    from App import App
1805
    import uuid
1806
    from LineNoTracer import LineNoTracer
1807
    from AppDocData import AppDocData
1808
    from EngineeringAbstractItem import QEngineeringAbstractItem
1809
    from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
1810
    from EngineeringInstrumentItem import QEngineeringInstrumentItem
1811
    from EngineeringReducerItem import QEngineeringReducerItem
1812
    from EngineeringEquipmentItem import QEngineeringEquipmentItem
1813
    from QEngineeringOPCItem import QEngineeringOPCItem
1814
    from EngineeringSpecBreakItem import QEngineeringSpecBreakItem
1815
    from EngineeringVendorItem import QEngineeringVendorItem
1816
    from EngineeringEndBreakItem import QEngineeringEndBreakItem
1817
    from EngineeringFlowMarkItem import QEngineeringFlowMarkItem
1818
    from EngineeringReservedWordTextItem import QEngineeringReservedWordTextItem
1819
    from QEngineeringSizeTextItem import QEngineeringSizeTextItem
1820
    from EngineeringValveOperCodeTextItem import QEngineeringValveOperCodeTextItem
1821
    from QEngineeringTrimLineNoTextItem import QEngineeringTrimLineNoTextItem
1822
    from QEngineeringTagNoTextItem import QEngineeringTagNoTextItem
1823
    from EngineeringErrorItem import QEngineeringErrorItem
1824
    from EngineeringTextItem import QEngineeringTextItem
1825
    from SpecialItemTypesDialog import SpecialItemTracer
1826
    from QEngineeringInstLineNoTextItem import QEngineeringInstLineNoTextItem
1827
    from EngineeringRunItem import QEngineeringRunItem
1828

    
1829
    try:
1830
        docdata = AppDocData.instance()
1831
        worker.display_message.emit('Initializing...')
1832

    
1833
        ALL_ITEM = [item for item in worker.scene.items() if issubclass(type(item), QEngineeringAbstractItem)]
1834
        for item in ALL_ITEM:
1835
            item.setVisible(False)
1836
        QApplication.processEvents()
1837

    
1838
        symbols = []
1839
        lines = [item for item in worker.scene.items() if type(item) is QEngineeringLineItem]
1840
        lineNos = []
1841
        equip_packages = []
1842
        end_breaks = []
1843

    
1844
        for error_item in [item for item in worker.scene.items() if type(item) is QEngineeringErrorItem]:
1845
            error_item.transfer.onRemoved.emit(error_item)
1846

    
1847
        for end_break in [item for item in worker.scene.items() if type(item) is QEngineeringEndBreakItem]:
1848
            if not end_break.prop('Freeze'):
1849
                end_break.transfer.onRemoved.emit(end_break)
1850
            else:
1851
                end_breaks.append(end_break)
1852

    
1853
        QApplication.processEvents()
1854

    
1855
        for item in worker.scene.items():
1856
            if issubclass(type(item), SymbolSvgItem) and not (type(item) is QEngineeringErrorItem) and not (
1857
                    type(item) is QEngineeringUnknownItem) and item.type != 'Notes' and not (
1858
                    type(item) is QEngineeringEndBreakItem):
1859
                symbols.append(item)
1860
            elif type(item) is QEngineeringLineNoTextItem:
1861
                if not item.runs:
1862
                    lineNos.append(item)
1863
            elif issubclass(type(item), QEngineeringTextItem):
1864
                item.owner = None
1865

    
1866
        lineNo_run = {}
1867
        for lineNo in lineNos:
1868
            lineNo_run[lineNo] = []
1869

    
1870
        QApplication.processEvents()
1871

    
1872
        # trace connected items
1873
        docdata._connected_items_lists = QEngineeringInstLineNoTextItem()
1874
        connected_items_lists = docdata._connected_items_lists
1875

    
1876
        items = symbols + lines
1877

    
1878
        ## find onwer line no
1879
        for item in symbols:
1880
            if item.owner is not None:
1881
                continue
1882
            
1883
            selected = None
1884
            minDist = None
1885
            for lineNo in lineNos:
1886
                dx = item.sceneBoundingRect().center().x() - lineNo.sceneBoundingRect().center().x()
1887
                dy = item.sceneBoundingRect().center().y() - lineNo.sceneBoundingRect().center().y()
1888
                length = math.sqrt(dx * dx + dy * dy)
1889

    
1890
                if minDist is None or length < minDist:
1891
                    minDist = length
1892
                    selected = lineNo
1893

    
1894
            #if selected:
1895
            #    lineNo_run[selected].append(item)
1896

    
1897
        for lineNo, run in lineNo_run.items():
1898
            for item in run:
1899
                item.owner = lineNo
1900

    
1901
            line_run = QEngineeringRunItem()
1902
            line_run.items = run
1903
            if line_run.items is not None and len(line_run.items) > 0:
1904
                line_run.owner = lineNo
1905
                lineNo.runs.append(line_run)
1906

    
1907
        ## find connected runs
1908
        while items:
1909
            connected_items_list = [items[0]]
1910
            connected_items_count = 0
1911
            while True:
1912
                if connected_items_count == len(connected_items_list):
1913
                    break
1914
                else:
1915
                    connected_items_count = len(connected_items_list)
1916

    
1917
                for item in items:
1918
                    if item in connected_items_list:
1919
                        matches = [conn.connectedItem for conn in item.connectors if conn.connectedItem and conn.connectedItem not in connected_items_list]
1920
                        if matches:
1921
                            connected_items_list.extend(matches)
1922
                            break
1923
                    else:
1924
                        matches = [conn for conn in item.connectors if conn.connectedItem and conn.connectedItem in connected_items_list]
1925
                        if matches:
1926
                            connected_items_list.append(item)
1927
                            break
1928

    
1929
            for item in connected_items_list:
1930
                items.remove(item)
1931
            
1932
            line_run = QEngineeringRunItem()
1933
            for run_item in connected_items_list:
1934
                line_run.items.append(run_item)
1935

    
1936
            line_run.owner = connected_items_lists
1937
            connected_items_lists.runs.append(line_run)
1938
        # up to here
1939

    
1940
        # connect attribute
1941
        worker.display_message.emit('Connecting Attribute...')
1942
        QApplication.processEvents()
1943
        texts = [item for item in worker.scene.items() if issubclass(type(item), QEngineeringTextItem)]
1944
        for symbol in symbols:
1945
            try:
1946
                symbol.connectAttribute(texts)
1947
            except Exception as ex:
1948
                message = 'error occurred({}) in {}:{}'.format(repr(ex), sys.exc_info()[-1].tb_frame.f_code.co_filename,
1949
                                                               sys.exc_info()[-1].tb_lineno)
1950
                worker.display_message.emit(message)
1951

    
1952
        """ try to connect label to valve """
1953
        labels = [symbol for symbol in symbols if symbol.iType == 19]  # Labels - Symbol => Instrument
1954
        valves = [symbol for symbol in symbols if
1955
                  symbol.iType == 34 or symbol.iType == 17 or symbol.iType == 22]  # Specialty Components, In-Line, Relief Devices
1956
        for label in labels:
1957
            label.connectAttribute(valves, clear=False)
1958

    
1959
        # for slope, etc.., symbol to line
1960
        labels = [symbol for symbol in symbols if symbol.iType == 30]  # Labels - Line => Piping
1961
        for label in labels:
1962
            label.connectAttribute(lines, clear=False)
1963

    
1964
        QApplication.processEvents()
1965
        for symbol in symbols:
1966
            for assoc in symbol.associations():
1967
                if assoc.owner is None and not issubclass(type(assoc), SymbolSvgItem):
1968
                    assoc.owner = symbol
1969

    
1970
        """ try to find text item's owner """
1971
        texts = [item for item in worker.scene.items() if type(item) is QEngineeringReservedWordTextItem]
1972
        for text in texts:
1973
            text.findOwner(lines)
1974

    
1975
        QApplication.processEvents()
1976
        # restore and save manual edited attr's text item
1977
        texts = [item for item in worker.scene.items() if issubclass(type(item), QEngineeringTextItem)]
1978
        targetText = []
1979
        for text in texts:
1980
            if text.owner:
1981
                continue
1982
            found = False
1983
            for symbol in symbols + lines:
1984
                for attr in list(symbol.attrs.keys()):
1985
                    # skip freezed attr or already consumed by symbol
1986
                    if attr.AssocItem == text:
1987
                        text.owner = symbol
1988
                        found = True
1989
                        break
1990
                    # restore missing attr
1991
                    elif type(attr.AssocItem) is str or type(attr.AssocItem) is uuid.UUID:
1992
                        if str(attr.AssocItem) == str(text.uid):
1993
                            symbol.add_assoc_item(text)
1994
                            attr.AssocItem = text
1995
                            text.owner = symbol
1996
                            found = True
1997
                        break
1998
                if found: break
1999
            if not found and (
2000
                    type(text) is QEngineeringSizeTextItem or type(text) is QEngineeringValveOperCodeTextItem or \
2001
                    type(text) is QEngineeringTagNoTextItem or type(text) is QEngineeringTextItem):
2002
                targetText.append(text)
2003

    
2004
        targetText = sorted(targetText, key=lambda param: len(param.text()), reverse=True)
2005
        for symbol in symbols + equip_packages:
2006
            symbol._skip = False
2007
        for text in targetText:
2008
            ret = text.findOwner(symbols) if type(text) is not QEngineeringTagNoTextItem else text.findOwner(
2009
                symbols + equip_packages)
2010
            if ret:
2011
                worker.add_update_text.emit(text, ret)
2012

    
2013
        QApplication.processEvents()
2014

    
2015
        ALL_ITEM = [item for item in worker.scene.items() if issubclass(type(item), QEngineeringAbstractItem)]
2016
        for item in ALL_ITEM:
2017
            item.setVisible(True)
2018

    
2019
    except Exception as ex:
2020
        message = 'error occurred({}) in {}:{}'.format(repr(ex), sys.exc_info()[-1].tb_frame.f_code.co_filename,
2021
                                                       sys.exc_info()[-1].tb_lineno)
2022
        worker.display_message.emit(message)
2023
    except:
2024
        (type1, value, traceback) = sys.exc_info()
2025
        sys.excepthook(type1, value, traceback)
클립보드 이미지 추가 (최대 크기: 500 MB)