def setValidator(self): wind = QRegExpValidator(QRegExp(self.rules.wind, Qt.CaseInsensitive)) self.wind.setValidator(wind) gust = QRegExpValidator(QRegExp(self.rules.gust, Qt.CaseInsensitive)) self.gust.setValidator(gust) vis = QRegExpValidator(QRegExp(self.rules.vis)) self.vis.setValidator(vis) cloud = QRegExpValidator(QRegExp(self.rules.cloud, Qt.CaseInsensitive)) self.cloud1.setValidator(cloud) self.cloud2.setValidator(cloud) self.cloud3.setValidator(cloud) self.cb.setValidator(cloud) weather = conf.value('Message/Weather') weathers = [''] + json.loads(weather) if weather else [''] self.weather.addItems(weathers) weatherWithIntensity = conf.value('Message/WeatherWithIntensity') intensityWeathers = [''] if weatherWithIntensity: for w in json.loads(weatherWithIntensity): intensityWeathers.extend(['-' + w, w, '+' + w]) self.weatherWithIntensity.addItems(intensityWeathers)
def generate(self): """生成 AFTN 电报格式的报文 :return: 报文列表 """ channel = conf.value('Communication/Channel') number = conf.value('Communication/ChannelSequenceNumber') number = int(number) if number else 0 level = 'FF' if self.reportType in ['SIGMET', 'AIRMET'] else 'GG' sendAddress = conf.value('Communication/{}Address'.format( self.reportType)) or '' originatorAddress = conf.value('Communication/OriginatorAddress') or '' groups = self.divideAddress(sendAddress) time = self.time.strftime('%d%H%M') origin = ' '.join([time, originatorAddress]) ending = 'NNNN' self.messages = [] for addr in groups: heading = ' '.join(['ZCZC', channel + str(number).zfill(4)]) address = ' '.join([level] + addr) items = [heading, address, origin] + self.text + [ending] items = self.formatLinefeed(items) self.messages.append(self.lineBreak.join(items)) number += 1 conf.setValue('Communication/ChannelSequenceNumber', str(number)) return self.messages
def head(self): area = conf.value('Message/Area') or '' icao = conf.value('Message/ICAO') time = self.time.strftime('%d%H%M') tt = self.type.tt messages = [tt + area, icao, time] return ' '.join(filter(None, messages))
def head(self): area = conf.value('Message/FIR').split()[0] sequence = self.sequence.text() beginningTime = self.beginningTime.text() endingTime = self.endingTime.text() icao = conf.value('Message/ICAO') text = '{} SIGMET {} VALID {}/{} {}-'.format(area, sequence, beginningTime, endingTime, icao) return text
def run(self): if conf.value('Monitor/WebApiURL'): url = conf.value('Monitor/WebApiURL') or 'http://127.0.0.1:6575' context.message.setState(remoteMessage(url)) if conf.value('Monitor/SelectedMobile'): url = conf.value( 'Monitor/CallServiceURL') or 'http://127.0.0.1:5000/api/call/' context.callService.setState(callService(url))
def setSound(self): self.ringSound = Sound('ring.wav', conf.value('Monitor/RemindTAFVolume')) self.notificationSound = Sound('notification.wav', 100) self.alarmSound = Sound('alarm.wav', conf.value('Monitor/WarnTAFVolume')) self.trendSound = Sound('trend.wav', conf.value('Monitor/RemindTrendVolume')) self.sigmetSound = Sound('sigmet.wav', conf.value('Monitor/RemindSIGMETVolume')) self.settingDialog.warnTafVolume.valueChanged.connect(lambda vol: self.alarmSound.play(volume=vol, loop=False)) self.settingDialog.remindTafVolume.valueChanged.connect(lambda vol: self.ringSound.play(volume=vol, loop=False)) self.settingDialog.remindTrendVolume.valueChanged.connect(lambda vol: self.trendSound.play(volume=vol, loop=False)) self.settingDialog.remindSigmetVolume.valueChanged.connect(lambda vol: self.sigmetSound.play(volume=vol, loop=False))
def __init__(self, text, reportType='TAF', time=None): super(AFTNMessage, self).__init__() self.text = text.split('\n') self.reportType = reportType self.time = datetime.datetime.utcnow() if time is None else time maxSendAddress = conf.value('Communication/MaxSendAddress') maxLineChar = conf.value('Communication/MaxLineChar') self.maxSendAddress = int( maxSendAddress) if maxSendAddress else 21 # AFTN 线路最大发电地址数 self.maxLineChar = int( maxLineChar) if maxLineChar else 69 # AFTN 线路每行最大字符数 self.lineBreak = '\n' self.generate()
def receive(self, message): self.message = message visHas5000 = boolean(conf.value('Validator/VisHas5000')) cloudHeightHas450 = boolean(conf.value('Validator/CloudHeightHas450')) try: self.parser = Parser(self.message['rpt'], visHas5000=visHas5000, cloudHeightHas450=cloudHeightHas450) self.parser.validate() html = '<p>{}<br/>{}</p>'.format(self.message['sign'], self.parser.renderer(style='html')) if self.parser.tips: html += '<p style="color: grey"># {}</p>'.format('<br/># '.join(self.parser.tips)) self.rpt.setHtml(html) self.message['rpt'] = self.parser.renderer() except Exception as e: logger.error(e)
def sign(self): area = conf.value('Message/Area') or '' icao = conf.value('Message/ICAO') time = self.date.text() tt = '' if self.fc.isChecked(): tt = 'FC' elif self.ft.isChecked(): tt = 'FT' ccc = self.ccc.text() if self.cor.isChecked() else None aaa = self.aaa.text() if self.amd.isChecked() else None aaaCnl = self.aaaCnl.text() if self.cnl.isChecked() else None messages = [tt + area, icao, time, ccc, aaa, aaaCnl] return ' '.join(filter(None, messages))
def message(self): fir = conf.value('Message/FIR') phenomena = self.weatherPhenomena() prediction = self.prediction() text = ' '.join([fir, phenomena, prediction]) return text
def setup(self): self.setWindowIcon(QIcon(':/logo.png')) # 初始化剪贴板 self.clip = QApplication.clipboard() # 闹钟提示框 self.remindBox = RemindMessageBox(self) # 初始化窗口 self.settingDialog = SettingDialog(self) self.tafSender = TafSender(self) self.trendSender = TrendSender(self) self.sigmetSender = SigmetSender(self) self.reSender = ReSender(self) self.tafEditor = TafEditor(self, self.tafSender) self.trendEditor = TrendEditor(self, self.trendSender) self.sigmetEditor = SigmetEditor(self, self.sigmetSender) if boolean(conf.value('General/Serious')): self.taskBrowser = TaskBrowser(self) self.taskTafSender = TaskTafSender(self) self.taskTafEditor = TaskTafEditor(self, self.taskTafSender) self.setRecent() self.setTable() self.setContractMenu() # 设置切换联系人菜单 self.setSysTray() self.setStatus() self.setThread() self.setSound()
def loadValue(self, path, option, category='text'): val = conf.value(path) option = getattr(self, option) if val is None: return 0 if category in ['text', 'plaintext']: option.setText(val) if category == 'bool': val = boolean(val) option.setChecked(val) if category == 'combox': index = option.findText(val, Qt.MatchFixedString) option.setCurrentIndex(index) if category == 'slider': option.setValue(int(val)) if category == 'list': try: items = json.loads(val) option.addItems(items) except (ValueError, TypeError): pass if category == 'mobile': person = db.query(User).filter_by(mobile=val).first() if person: index = option.findText(person.name, Qt.MatchFixedString) option.setCurrentIndex(index)
def keyPressEvent(self, event): if boolean(conf.value('General/Serious')): if event.modifiers() == (Qt.ShiftModifier | Qt.ControlModifier): if event.key() == Qt.Key_P: self.taskTafEditor.show() if event.key() == Qt.Key_T: self.taskBrowser.show()
def closeEvent(self, event): if event.spontaneous(): event.ignore() self.hide() else: self.tafSender.setAttribute(Qt.WA_DeleteOnClose) self.trendSender.setAttribute(Qt.WA_DeleteOnClose) self.sigmetSender.setAttribute(Qt.WA_DeleteOnClose) self.reSender.setAttribute(Qt.WA_DeleteOnClose) self.tafEditor.setAttribute(Qt.WA_DeleteOnClose) self.trendEditor.setAttribute(Qt.WA_DeleteOnClose) self.sigmetEditor.setAttribute(Qt.WA_DeleteOnClose) self.settingDialog.setAttribute(Qt.WA_DeleteOnClose) self.tafSender.close() self.trendSender.close() self.sigmetSender.close() self.reSender.close() self.tafEditor.close() self.trendEditor.close() self.sigmetEditor.close() self.settingDialog.close() if boolean(conf.value('General/Serious')): self.taskTafSender.setAttribute(Qt.WA_DeleteOnClose) self.taskTafEditor.setAttribute(Qt.WA_DeleteOnClose) self.taskBrowser.setAttribute(Qt.WA_DeleteOnClose) self.taskTafSender.close() self.taskTafEditor.close() self.taskBrowser.close() self.tray.hide() event.accept()
def painter(self): utc = datetime.datetime.utcnow() nextTime = utc.replace(microsecond=0, second=0, minute=0) + datetime.timedelta(hours=1, minutes=10) delta = nextTime - utc QTimer.singleShot(delta.total_seconds() * 1000, self.painter) if conf.value('Monitor/FirApiURL'): self.firInfoThread.start()
def main(): import sys app = QApplication(sys.argv) translator = QTranslator() locale = QLocale.system().name() translateFile = os.path.join(BASEDIR, 'i18n\\translations', '{}.qm'.format(locale)) if translator.load(translateFile): app.installTranslator(translator) # QApplication.setStyle(QStyleFactory.create('Fusion')) if boolean(conf.value('General/LargeFont')): font = QFont('Courier New', 14) app.setFont(font) serverName = 'Tafor' socket = QLocalSocket() socket.connectToServer(serverName) # 如果连接成功,表明server已经存在,当前已有实例在运行 if socket.waitForConnected(500): return(app.quit()) # 没有实例运行,创建服务器 localServer = QLocalServer() localServer.listen(serverName) try: window = MainWindow() window.show() sys.exit(app.exec_()) except Exception as e: logger.error(e, exc_info=True) finally: localServer.close()
def setFont(self): font = QFont('Segoe UI') if boolean(conf.value('General/LargeFont')): font.setPointSize(15) else: font.setPixelSize(14) self.rpt.setFont(font)
def updateContractMenu(self): mobile = conf.value('Monitor/SelectedMobile') person = db.query(User).filter_by(mobile=mobile).first() if person: action = getattr(self, 'contract' + str(person.id), None) if action: action.setChecked(True) else: self.contractNo.setChecked(True)
def receive(self, message): self.message = message firCode = conf.value('Message/FIR') try: self.parser = SigmetParser(self.message['rpt'], firCode=firCode) html = '<p>{}<br/>{}</p>'.format( self.message['sign'], self.parser.renderer(style='html')) self.rpt.setHtml(html) except Exception as e: logger.error(e)
def setText(self): last = db.query(Sigmet).filter(Sigmet.tt == self.type.tt, ~Sigmet.rpt.contains('CNL')).order_by(Sigmet.sent.desc()).first() if last: fir = conf.value('Message/FIR') _, text = last.rpt.split('\n') text = text.replace(fir, '') text = text.replace('=', '').strip() self.content.text.setText(text) else: self.content.text.clear()
def actionController(self): alwaysShow = boolean(conf.value('General/AlwaysShowEditor')) if not alwaysShow: self.previewSignal.connect(self.hide) self.previewSignal.connect(self.sender.receive) self.previewSignal.connect(self.sender.show) self.sender.sendSignal.connect(self.parent.updateGui) self.sender.backSignal.connect(self.show) self.sender.closeSignal.connect(self.close)
def remindSigmet(self): remindSwitch = boolean(conf.value('Monitor/RemindSIGMET')) if not remindSwitch: return None text = QCoreApplication.translate('MainWindow', 'Time to post {}').format('SIGMET') self.sigmetSound.play() self.remindBox.setText(text) ret = self.remindBox.exec_() if not ret: QTimer.singleShot(1000 * 60 * 5, self.remindSigmet) self.sigmetSound.stop()
def run(self): port = conf.value('Communication/SerialPort') baudrate = int(conf.value('Communication/SerialBaudrate')) bytesize = conf.value('Communication/SerialBytesize') parity = conf.value('Communication/SerialParity') stopbits = conf.value('Communication/SerialStopbits') try: context.serial.lock() serialComm(self.message, port, baudrate=baudrate, bytesize=bytesize, parity=parity, stopbits=stopbits) error = '' except Exception as e: error = str(e) logger.error(e) finally: context.serial.release() self.doneSignal.emit(error)
def singer(self): warnSwitch = self.warnTafAction.isChecked() trendSwitch = boolean(conf.value('Monitor/RemindTrend')) # 管理趋势声音 utc = datetime.datetime.utcnow() if trendSwitch and utc.minute in (58, 59): self.trendSound.play() else: self.trendSound.stop() # 管理报文告警声音 if warnSwitch and context.taf.isWarning(): self.alarmSound.play() else: self.alarmSound.stop()
def message(self): super(TafPrimarySegment, self).message() amd = 'AMD' if self.amd.isChecked() or self.cnl.isChecked() else '' cor = 'COR' if self.cor.isChecked() else '' icao = conf.value('Message/ICAO') timez = self.date.text() + 'Z' period = self.period.text() tmax = ''.join(['TX', self.tmax.text(), '/', self.tmaxTime.text(), 'Z']) tmin = ''.join(['TN', self.tmin.text(), '/', self.tminTime.text(), 'Z']) if self.cnl.isChecked(): messages = ['TAF', amd, icao, timez, period, 'CNL'] else: messages = ['TAF', amd, cor, icao, timez, period, self.msg, tmax, tmin] self.msg = ' '.join(filter(None, messages)) return self.msg
def area(self): if self.latitudeAndLongitude.isChecked(): north = 'N OF {}'.format(self.north.text()) if self.north.text() else '' south = 'S OF {}'.format(self.south.text()) if self.south.text() else '' east = 'E OF {}'.format(self.east.text()) if self.east.text() else '' west = 'W OF {}'.format(self.west.text()) if self.west.text() else '' areas = [north, south, east, west] text = ' AND '.join(filter(None, areas)) if self.points.isChecked(): text = self.pointsWidget.text() if self.local.isChecked(): text = conf.value('Message/ICAO') return text
def hasExpired(self, offset=None): """当前时段报文是否过了有效发报时间 :param offset: 过期时间,单位分钟 """ offset = offset or conf.value('Monitor/WarnTAFTime') offset = int(offset) if offset else 30 offsetTimeDelta = { 'FC': datetime.timedelta(minutes=offset), 'FT': datetime.timedelta(hours=2, minutes=offset) } timedelta = offsetTimeDelta.get(self.tt) period = self.warningPeriod(withDay=False) start = self.startTime.get(period) threshold = start + timedelta return threshold < self.time
def remindTaf(self, tt): remindSwitch = boolean(conf.value('Monitor/RemindTAF')) if not remindSwitch: return None state = context.taf.state() clock = state[tt]['clock'] period = state[tt]['period'] sent = state[tt]['sent'] warnning = state[tt]['warnning'] if clock and not warnning and not sent: current = tt + period[2:] text = QCoreApplication.translate('MainWindow', 'Time to post {}').format(current) self.ringSound.play() self.remindBox.setText(text) ret = self.remindBox.exec_() if not ret: QTimer.singleShot(1000 * 60 * 5, lambda: self.remindTaf(tt)) self.ringSound.stop()
def exportConf(self, defaultPath=False): if defaultPath: filename = os.path.join(BASEDIR, 'default.json') paths = [option[0] for option in self.options] options = {path: conf.value(path) for path in paths} path = self.exportPath.text() if path: filedir, _= os.path.split(path) if os.path.isdir(filedir): filename = self.exportPath.text() try: with open(filename, 'w') as file: json.dump(options, file) except Exception as e: pass else: self.parent.statusBar.showMessage(QCoreApplication.translate('Settings', 'Configuration has been exported'), 5000)
def isConfigured(reportType='TAF'): """检查发布不同类型报文基础配置是否完成""" serial = ['Communication/SerialPort', 'Communication/SerialBaudrate', 'Communication/SerialParity', 'Communication/SerialBytesize', 'Communication/SerialStopbits'] aftn = ['Communication/Channel', 'Communication/ChannelSequenceNumber', 'Communication/MaxLineChar', 'Communication/MaxSendAddress', 'Communication/OriginatorAddress'] taf = ['Message/ICAO', 'Message/Area', 'Communication/TAFAddress'] trend = ['Message/TrendSign', 'Communication/TrendAddress'] sigmet = ['Message/ICAO', 'Message/FIR', 'Message/Area', 'Communication/SIGMETAddress'] options = serial + aftn if reportType == 'TAF': options += taf if reportType == 'Trend': options += trend if reportType == 'SIGMET': options += sigmet values = [conf.value(path) for path in options] return all(values)