From f6720ad7ce5fedc2a48065531571f59823a3fe20 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Thu, 18 Sep 2025 13:20:55 +0800 Subject: [PATCH 1/5] =?UTF-8?q?feat:=20=E7=8E=BB=E7=BA=A4=E6=8B=89?= =?UTF-8?q?=E4=B8=9D=E9=87=87=E9=9B=86=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- out_service/insert_kvt.py | 143 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 out_service/insert_kvt.py diff --git a/out_service/insert_kvt.py b/out_service/insert_kvt.py new file mode 100644 index 00000000..0fb3f09f --- /dev/null +++ b/out_service/insert_kvt.py @@ -0,0 +1,143 @@ +import requests +import os +import sys +import django +import json +import logging +import time +import threading + +CUR_DIR = os.path.dirname(os.path.abspath(__file__)) +BASE_DIR = os.path.dirname(CUR_DIR) +sys.path.insert(0, BASE_DIR) + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "server.settings") +django.setup() + +from apps.enm.services import insert_mplogx_item +from django.utils import timezone +from apps.utils.tasks import send_mail_task +from datetime import datetime, timedelta +CUR_DIR = os.path.dirname(os.path.abspath(__file__)) +BASE_DIR = os.path.dirname(CUR_DIR) +sys.path.insert(0, BASE_DIR) + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "server.settings") +django.setup() + +SERVER = '192.168.1.37' +PORT = '8089' +PATH = f'http://{SERVER}:{PORT}/GetTagList' + +SERVER2 = '192.168.1.43' +PORT2 = '8081' +PATH2 = f'http://{SERVER2}:{PORT2}/GetTagList' + +#MIN_LIST = set(range(0,61,5)) +MIN_LIST = [0] +myLogger = logging.getLogger("log") + + +class MailController: + def __init__(self): + self.last_sent_time = None + self.interval = timedelta(days=1) + self.lock = threading.Lock() + self._is_sending = False + + def should_send_mail(self): + now = datetime.now() + # thread_name = threading.current_thread().name + with self.lock: + if self._is_sending: + # myLogger.info(f"线程 {thread_name}: 邮件正在发送中,跳过") + return False + if self.last_sent_time is None: + # myLogger.info(f"线程 {thread_name}: 首次发送邮件") + self._is_sending = True + return True + time_since_last = now - self.last_sent_time + if time_since_last > self.interval: + # myLogger.info(f"线程 {thread_name}: 距离上次发送已超过间隔,允许发送") + self._is_sending = True + return True + else: + # myLogger.info(f"线程 {thread_name}: 距离上次发送不足间隔,跳过") + return False + + def mark_as_sent(self): + with self.lock: + self.last_sent_time = datetime.now() + self._is_sending = False + myLogger.info("邮件发送状态已重置") + + +mail_controller = MailController() + + +def send_error_notification(error_message): + """ + 发送错误通知 + """ + if mail_controller.should_send_mail(): + try: + send_mail_task.delay(subject='insert_kvt_error', message=str(error_message)) + myLogger.error(f"请求组态王失败:{str(error_message)}") + except Exception as e: + myLogger.exception(f"发送错误邮件失败: {e}") + finally: + mail_controller.mark_as_sent() + +def fetch_data(timex, enp_mpoint_dict, path): + """ + 从数据库转存到超表 + """ + response = None + try: + response = requests.get(path, timeout=5) + response.raise_for_status() # 如果响应码不是 200,将触发异常 + except requests.RequestException as e: + send_error_notification(e) + + if response is None: + return + try: + lines = response.text + json_line = [line.strip() for line in lines.split('\n') if line.strip() ] + current_object = [] + # 将碎片分组为完整的 JSON 对象 + for line in json_line: + current_object.append(line.strip()) # 将当前行加入对象 + if line.strip() == '}': # 遇到结束大括号时 + try: + obj_str = ' '.join(current_object).replace(',}', '}') + obj_dict = json.loads(obj_str) + insert_mplogx_item(obj_dict.get('strVarName'), obj_dict.get('VarValue'), timex, enp_mpoint_dict) + except json.JSONDecodeError as e: + send_error_notification(e) + current_object = [] # 重置,准备处理下一个对象 + except Exception as e: + send_error_notification(e) + +def get_data(): + last_triggered = None + while True: + now = timezone.now() + now = now.replace(microsecond=0) + if now.second in MIN_LIST: + if last_triggered != now: + last_triggered = now + enp_mpoint_dict= {} + threads = [ + threading.Thread(target=fetch_data, args=(now, enp_mpoint_dict, PATH), daemon=True), + threading.Thread(target=fetch_data, args=(now, enp_mpoint_dict, PATH2), daemon=True) + ] + for t in threads: + t.start() + for t in threads: + t.join(timeout=10) # 设置超时防止线程挂起 + + time.sleep(0.5) + +if __name__ == '__main__': + get_data() From f6668b5d38494a1ffbd424f3117811717a44caf2 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Thu, 18 Sep 2025 14:48:28 +0800 Subject: [PATCH 2/5] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96handover=20list?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wpm/serializers.py | 8 ++++++++ apps/wpm/views.py | 10 +++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/apps/wpm/serializers.py b/apps/wpm/serializers.py index 5c0f8489..dcd66916 100644 --- a/apps/wpm/serializers.py +++ b/apps/wpm/serializers.py @@ -1149,6 +1149,12 @@ class HandoverbSerializer(CustomModelSerializer): read_only_fields = EXCLUDE_FIELDS_BASE + ['handover'] extra_kwargs = {'wm': {'required': True}} +class HandoverbListSerializer(serializers.Serializer): + defect_name = serializers.CharField(source="wm.defect.name", read_only=True) + class Meta: + model = Handoverb + fields = "__all__" + class HandoverSerializer(CustomModelSerializer): # wm = serializers.PrimaryKeyRelatedField( # label='车间库存ID', queryset=WMaterial.objects.all()) @@ -1363,6 +1369,8 @@ class HandoverUpdateSerializer(CustomModelSerializer): model = Handover fields = ['id', 'send_date', 'send_user', 'count', 'count_eweight', 'recive_user', 'note'] +class HandoverListSerializer(HandoverSerializer): + handoverb = HandoverbListSerializer(many=True, required=False) class GenHandoverSerializer(serializers.Serializer): diff --git a/apps/wpm/views.py b/apps/wpm/views.py index 8944e409..e2aaec22 100644 --- a/apps/wpm/views.py +++ b/apps/wpm/views.py @@ -15,7 +15,7 @@ from apps.utils.mixins import CustomListModelMixin, BulkCreateModelMixin, Comple from .filters import StLogFilter, SfLogFilter, WMaterialFilter, MlogFilter, HandoverFilter, MlogbFilter, BatchStFilter, MlogbwFilter from .models import (SfLog, SfLogExp, StLog, WMaterial, Mlog, Handover, Mlogb, - Mlogbw, AttLog, OtherLog, Fmlog, BatchSt, MlogbDefect, MlogUser, BatchLog) + Mlogbw, AttLog, OtherLog, Fmlog, BatchSt, MlogbDefect, MlogUser, BatchLog, Handoverb) from .serializers import (SflogExpSerializer, SfLogSerializer, StLogSerializer, WMaterialSerializer, MlogRevertSerializer, MlogSerializer, MlogRelatedSerializer, DeptBatchSerializer, HandoverSerializer, @@ -25,7 +25,8 @@ from .serializers import (SflogExpSerializer, SfLogSerializer, StLogSerializer, MlogbDetailSerializer, MlogbInSerializer, MlogbInUpdateSerializer, MlogbOutUpdateSerializer, FmlogSerializer, FmlogUpdateSerializer, BatchStSerializer, MlogbwCreateUpdateSerializer, HandoverMgroupSerializer, MlogListSerializer, - MlogbSerializer, MlogUserSerializer, BatchLogSerializer, MlogQuickSerializer, MlogbwStartTestSerializer) + MlogbSerializer, MlogUserSerializer, BatchLogSerializer, MlogQuickSerializer, + MlogbwStartTestSerializer, HandoverListSerializer) from .services import mlog_submit, handover_submit, mlog_revert, get_batch_dag, handover_revert from apps.wpm.services import mlog_submit_validate, generate_new_batch from apps.wf.models import State, Ticket @@ -493,12 +494,15 @@ class HandoverViewSet(CustomModelViewSet): 交接记录 """ queryset = Handover.objects.all() + list_serializer_class = HandoverListSerializer serializer_class = HandoverSerializer select_related_fields = ['send_user', 'send_mgroup', 'send_dept', 'recive_user', 'recive_mgroup', 'recive_dept', 'wm'] filterset_class = HandoverFilter search_fields = ['id', 'material__name', 'material__number', 'material__specification', 'batch', 'material__model', 'b_handover__batch', "new_batch", "wm__batch"] - prefetch_related_fields = ["b_handover"] + prefetch_related_fields = [ + Prefetch('b_handover', queryset=Handoverb.objects.select_related('wm__defect')) + ] def perform_destroy(self, instance): user = self.request.user From 085978a6c4467205dd41c20ad14a884270e77f12 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Thu, 18 Sep 2025 15:06:45 +0800 Subject: [PATCH 3/5] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96handover=20list?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wpm/serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/wpm/serializers.py b/apps/wpm/serializers.py index dcd66916..0e4bbc6a 100644 --- a/apps/wpm/serializers.py +++ b/apps/wpm/serializers.py @@ -1149,7 +1149,7 @@ class HandoverbSerializer(CustomModelSerializer): read_only_fields = EXCLUDE_FIELDS_BASE + ['handover'] extra_kwargs = {'wm': {'required': True}} -class HandoverbListSerializer(serializers.Serializer): +class HandoverbListSerializer(CustomModelSerializer): defect_name = serializers.CharField(source="wm.defect.name", read_only=True) class Meta: model = Handoverb From 4390992a141e2ad28af3d2af650b11c3c31b8c78 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Fri, 19 Sep 2025 09:09:44 +0800 Subject: [PATCH 4/5] =?UTF-8?q?feat:=20base=20workflow=E6=B7=BB=E5=8A=A0vi?= =?UTF-8?q?ew=5Fpath?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wf/migrations/0003_workflow_view_path.py | 18 ++++++++++++++++++ apps/wf/models.py | 1 + 2 files changed, 19 insertions(+) create mode 100644 apps/wf/migrations/0003_workflow_view_path.py diff --git a/apps/wf/migrations/0003_workflow_view_path.py b/apps/wf/migrations/0003_workflow_view_path.py new file mode 100644 index 00000000..9d741bd4 --- /dev/null +++ b/apps/wf/migrations/0003_workflow_view_path.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.12 on 2025-09-19 01:08 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('wf', '0002_alter_state_filter_dept'), + ] + + operations = [ + migrations.AddField( + model_name='workflow', + name='view_path', + field=models.TextField(blank=True, null=True, verbose_name='前端自定义页面路径'), + ), + ] diff --git a/apps/wf/models.py b/apps/wf/models.py index 19a06f13..39730a1d 100755 --- a/apps/wf/models.py +++ b/apps/wf/models.py @@ -21,6 +21,7 @@ class Workflow(CommonAModel): '标题模板', max_length=50, default='{title}', null=True, blank=True, help_text='工单字段的值可以作为参数写到模板中,格式如:你有一个待办工单:{title}') content_template = models.CharField( '内容模板', max_length=1000, default='标题:{title}, 创建时间:{create_time}', null=True, blank=True, help_text='工单字段的值可以作为参数写到模板中,格式如:标题:{title}, 创建时间:{create_time}') + view_path = models.TextField('前端自定义页面路径', null=True, blank=True) class Meta: verbose_name = '工作流' From 47ca272ceea90c101a117f2dcb59dc51ea64c979 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Fri, 19 Sep 2025 09:20:02 +0800 Subject: [PATCH 5/5] =?UTF-8?q?feat:=20base=20workflow=20list=20=E8=BF=94?= =?UTF-8?q?=E5=9B=9Eview=5Fpath?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wf/serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/wf/serializers.py b/apps/wf/serializers.py index 519a31c4..738bb4d9 100755 --- a/apps/wf/serializers.py +++ b/apps/wf/serializers.py @@ -27,7 +27,7 @@ class StateSerializer(CustomModelSerializer): class WorkflowSimpleSerializer(CustomModelSerializer): class Meta: model = Workflow - fields = ['id', 'name', 'key'] + fields = ['id', 'name', 'key', 'view_path'] class StateSimpleSerializer(CustomModelSerializer):