def add_plan_operation_log(self, location): ''' 添加信号楼日志 :return: ''' LogManage.put_log(content="消息回调处理异常{error}".format(error=callback_error), mode="socketio_log")
def sync_manual(self): ''' 手动同步, 与ats进行同步 { cmd: "get_time_table", data: { "date": '2019-08-13' } }, { cmd: "get_cur_train_info", data: { "location": 99, # 99 高大路, 98板桥 } } :return: ''' today_obj = pendulum.today("UTC").add(hours=8) today_str = today_obj.format("YYYY-MM-DD") ban_qiao = self.env.ref("metro_park_base_data_10.ban_qiao").id gao_da_lu = self.env.ref("metro_park_base_data_10.gao_da_lu").id self.env["metro_park_dispatch.msg_client"] \ .get_time_tables([{ "location": ban_qiao, "date": today_str }, { "location": gao_da_lu, "date": today_str }]) LogManage.put_log(content='手动同步', mode='manual_sync') raise exceptions.Warning("命令已发送,请等待ats回传信息")
async def on_connect(): ''' 连接成功 :return: ''' _logger.info('connect to app server success!') LogManage.put_log(content="socketio 客户端已经联接", mode="socketio_client_log")
async def disconnect(): ''' 断开连接 :return: ''' _logger.info('the app server connect is dropped!') LogManage.put_log(content="socketio 客户端断开!", mode="socketio_client_log")
async def execute_msg(msg): ''' 后台任务发送消息 :return: ''' count = 0 try: if msg["msg_type"] and msg["data"]: _logger.info("begin deal socketio msg...!") room = msg.get("room", None) callback_info = msg.get("callback_info", None) if callback_info: def call_back(_callback_info, *args, **kwargs): try: res_ids = _callback_info.get('ids', []) context = _callback_info.get('context', []) model_env.deal_event(data={ "msg_type": "call", "uid": _callback_info.get("uid", None), "room": room, "model": _callback_info["model"], "name": _callback_info["name"], 'res_ids': res_ids, "args": args, "kwargs": kwargs, 'context': context }) except Exception as callback_error: import traceback traceback.print_exc() _logger.error(callback_error) LogManage.put_log(content="消息回调处理异常{error}".format(error=callback_error), mode="socketio_log") await sio.emit(msg["msg_type"], data=msg["data"], to=msg.get("sid", None), callback=partial(call_back, copy.deepcopy(callback_info)), room=room) else: await sio.emit(msg["msg_type"], data=msg["data"], to=msg.get('sid', None), room=room) _logger.info("deal socketio msg finished...!") # 用于查看后台是否正常运行 if count > 50: count = 0 LogManage.put_log(content="socketio 后台服务处理数据完成!", mode="socketio_log") else: count += 1 except Exception as tmp_error: import traceback traceback.print_exc() _logger.error(tmp_error) LogManage.put_log(content="消息处理异常{error}".format(error=tmp_error), mode="socketio_log")
def _update_last_login(self): ''' 登录成功之后将当日失败次数清零,清空失败起始时间,限制时间回到1分 :return: ''' super()._update_last_login() LogManage.put_log(content='用户登录系统', mode='login') self.env.user.write({ 'today_continuous_error_count': 0, 'no_login_minute': 1, 'no_login_start_time': None })
async def inner_run(): ''' 内部运行 :return: ''' socket_host = config.options.get('socket_io_app_server', '') or '0.0.0.0:9080' url = 'http://' + socket_host _logger.info('connect to socketio server, address %s' % url) LogManage.put_log(content='connect to socketio server, address %s' % url, mode="socketio_client_log") # 服务端有可能还没启动,client_io在第一次连接成功以后才会重联 connected = False await client_io.connect(url) await client_io.wait() LogManage.put_log(content="客户退退出!", mode="socketio_client_log")
def get_sio_cookies(cls, environ): ''' 从sio取得cookie :param environ: :return: ''' try: cookie = {} if 'HTTP_COOKIE' in environ and environ['HTTP_COOKIE']: http_cookie = environ['HTTP_COOKIE'].strip().split(';') for ck in http_cookie: k, v = ck.strip().split('=') cookie[k] = v return cookie return None except Exception as tmp_error: _logger.info( "can not get cookie: {environ}".format(environ=environ)) LogManage.put_log(content='处理cookie发生错误 {error}'.format(error=tmp_error), mode="socketio_log")
def call_back(_callback_info, *args, **kwargs): try: res_ids = _callback_info.get('ids', []) context = _callback_info.get('context', []) model_env.deal_event(data={ "msg_type": "call", "uid": _callback_info.get("uid", None), "room": room, "model": _callback_info["model"], "name": _callback_info["name"], 'res_ids': res_ids, "args": args, "kwargs": kwargs, 'context': context }) except Exception as callback_error: import traceback traceback.print_exc() _logger.error(callback_error) LogManage.put_log(content="消息回调处理异常{error}".format(error=callback_error), mode="socketio_log")
def init_cur_train(self): ''' 初始化现车, 这里应当是调用ats的现车信息 :return: ''' trains = self.env['metro_park_maintenance.train_dev'].search([]) train_cache = {train.id: train for train in trains} train_ids = trains.ids records = self.search([]) # 当前车辆的id cur_train_ids = records.mapped("train.id") vals = [] for tmp_id in train_ids: if tmp_id not in cur_train_ids: val = { 'train': tmp_id, 'train_status': 'wait', 'cur_location': train_cache[tmp_id].location.id, 'old_status': 'wait', } vals.append(val) self.create(vals) LogManage.put_log(content='同步车辆', mode='synchronous_vehicles')
def get_uid_id_from_sio(cls, environ): ''' 取得session_id, 用于从odoo查询用户信息 :param environ: :return: ''' cookies = cls.get_sio_cookies(environ) if cookies: session_id = cookies.get("session_id", None) if not session_id: return False try: session_info = root.session_store.get(session_id) if not session_info: _logger.info("the user is login out!") return False except Exception as tmp_error: _logger.info("get session store error, {error}".format(error=tmp_error)) LogManage.put_log(content='get session store error {error}'.format(error=tmp_error), mode="socketio_log") uid = session_info.get('context', {}).get("uid", False) return uid else: return False
def notify_train_position_changed(self, rail_location=None): ''' 模拟车辆到达 :return: ''' self.ensure_one() if self.cur_switch or self.cur_rail: alias = self.cur_location.alias state = self.make_position_info() self.trigger_up_event('funenc_socketio_server_msg', data={ "msg_type": "update_train_position", "location_alias": alias, "msg_data": [state] }) LogManage.put_log(content='通知车{name}到达位置{position}'.format( name=state["name"], position=state["position"]), mode='cur_train_position_change') else: try: # 可能是后台来的,没法区分用户 alias = self.env.user.cur_location.alias or rail_location state = self.make_position_info() self.trigger_up_event('funenc_socketio_server_msg', data={ "msg_type": "update_train_position", "location_alias": alias, "msg_data": [state] }) LogManage.put_log( content='通知移除{name}位置'.format(name=state["name"]), mode='cur_train_position_change') except Exception as error: _logger.info( "update position error!{error}".format(error=error))
def deal_event(self, data=None): """ 处理事件消息,类似rpc :param data: :return: """ LogManage.put_log(content="收到中转服务器请求", mode="socketio_client_log") try: with api.Environment.manage(), self.db_registry.cursor() as cr: env = api.Environment(cr, SUPERUSER_ID, {}) model_name = data.get('model', False) model = env[model_name] args = data.get('args', []) kwargs = data.get('kwargs', {}) data_rec = getattr(model, data.get('method'))(*args, **kwargs) LogManage.put_log(content="socketio客户端数据处理完成", mode="socketio_client_log") return data_rec except Exception as error: LogManage.put_log(content="处理中转服务器请求出错{error}".format(error=error), mode="socketio_client_log") return False
def on_ok(self): ''' 导入运行图 :return: ''' cur_location = self.env.user.cur_location if not cur_location: raise exceptions.ValidationError("当前用户没有配置位置信息。") main_line = self.env.ref( "metro_park_base_data_6.main_line_location").id hui_long = self.env.ref("metro_park_base_data_6.hui_long").id pi_tong = self.env.ref("metro_park_base_data_6.pi_tong").id time_table_model = self.env['metro_park_base.time_table'] locations_model = self.env['metro_park_base.location'] time_table_data_model = self.env['metro_park_base.time_table_data'] # 分为从时刻表导入和从运行图概要导入 if self.type == 'run_plan': time_table = time_table_model.create({ 'no': self.no, 'time_table_type': self.time_table_type, }) datas = self.read_excel() # 检查表头 if len(datas) < 2: raise exceptions.ValidationError('数据表格式不正确,行数小于2') # 检查表头 header_row = datas[0] if header_row[INDEX_COL] != '序号': raise exceptions.Warning('表头"序号"不正确, 请修正后再导入!') if header_row[TRAIN_NO_COL] != '车底ID': raise exceptions.Warning('表头"车底ID"不正确, 请修正后再导入!') if header_row[PLAN_OUT_LOCATION_COL] != '出库位置': raise exceptions.Warning('表头"出库位置"不正确, 请修正后再导入!') if header_row[PLAN_OUT_TIME_COL] != '出车场时间': raise exceptions.Warning('表头"出车场时间"不正确, 请修正后再导入!') if header_row[PLAN_BACK_LOCATION_COL] != '入库位置': raise exceptions.Warning('表头"入库位置"不正确, 请修正后再导入!') if header_row[PLAN_BACK_TIME_COL] != '回车场时间': raise exceptions.Warning('表头"回车场时间"时间不正确, 请修正后再导入!') # 取得所有的场段名称 valid_rows = [] locations_ar = [] for row in datas: if len(row) > 1: index = row[0] if re.match(r'^\d+(.0)?$', str(index)): valid_rows.append(row) locations_ar.append(row[PLAN_OUT_LOCATION_COL]) locations_ar.append(row[PLAN_BACK_LOCATION_COL]) locations = locations_model.search([('name', 'in', locations_ar)]) if len(locations) == 0: raise exceptions.ValidationError("没有找到位置数据!") location_cache = { location['name']: location['id'] for location in locations } vals = [] for row in valid_rows: out_location = row[PLAN_OUT_LOCATION_COL].strip() back_location = row[PLAN_BACK_LOCATION_COL].strip() # 只要有一个就添加数据 if out_location in location_cache or back_location in location_cache: if out_location not in location_cache: out_location = main_line else: out_location = location_cache.get(out_location) if back_location not in location_cache: back_location = main_line else: back_location = location_cache.get(back_location) # 转换为utc时间 plan_out_time = pendulum.parse('2019-01-01 ' + str(row[PLAN_OUT_TIME_COL])) plan_out_time = plan_out_time.subtract(hours=8) plan_back_time = pendulum.parse( '2019-01-01 ' + str(row[PLAN_BACK_TIME_COL])) plan_back_time = plan_back_time.subtract(hours=8) # 时间格式检验 vals.append({ 'sequence': int(row[INDEX_COL]), 'train_no': str(row[TRAIN_NO_COL]), 'out_location': out_location, 'plan_out_time': plan_out_time.format( 'YYYY-MM-DD HH:mm:ss'), # 这里时间查询时要注意 'plan_in_time': plan_back_time.format( 'YYYY-MM-DD HH:mm:ss'), # 这里时间查询时要注意 'back_location': back_location, 'time_table_id': time_table['id'] }) if len(vals) == 0: raise exceptions.ValidationError('没有导入任何数据,请检查文件是否正确') time_table_data_model.create(vals) LogManage.put_log(content='导入运行图', mode='import_diagram') return False else: time_table = time_table_model.create({ 'no': self.no, 'time_table_type': self.time_table_type, }) bin_data = base64.b64decode(self.xls_file) workbook = xlrd.open_workbook(file_contents=bin_data) sheet = workbook.sheet_by_index(0) vals = [] for i in range(1, sheet.nrows): data = sheet.row_values(i) if data[4] == "回龙停车场": out_location = hui_long elif data[4] == "郫筒": out_location = pi_tong else: out_location = main_line if data[5] == "回龙停车场": back_location = hui_long elif data[5] == "郫筒": back_location = pi_tong else: back_location = main_line plan_out_time = pendulum.parse('2019-01-01 ' + str(data[6])) plan_out_time = plan_out_time.subtract(hours=8) plan_back_time = pendulum.parse('2019-01-01 ' + str(data[8])) if plan_back_time.hour < 3: plan_back_time = pendulum.parse('2019-01-03 ' + str(data[8])) plan_back_time = plan_back_time.subtract(hours=8) vals.append({ 'sequence': data[0], 'train_no': data[1], 'out_location': out_location, 'plan_out_time': plan_out_time.format('YYYY-MM-DD HH:mm:ss'), # 这里时间查询时要注意 'plan_in_time': plan_back_time.format('YYYY-MM-DD HH:mm:ss'), # 这里时间查询时要注意 'back_location': back_location, 'time_table_id': time_table['id'], 'miles': data[11] }) if len(vals) == 0: raise exceptions.ValidationError('没有导入任何数据,请检查文件是否正确') time_table_data_model.create(vals) LogManage.put_log(content='导入车底运用指标', mode='import_diagram')
# -*- coding: utf-8 -*- import logging import threading from queue import Queue from odoo.addons.odoo_operation_log.model_extend import LogManage from odoo import models, api from odoo import registry from odoo.models import SUPERUSER_ID from odoo.service.server import server from odoo.tools import config LogManage.register_type('socketio_client_log', "socketio client日志") _logger = logging.getLogger(__name__) message_queue = Queue() client_transfer_channel = config.get('client_transfer_channel', 'FJJQLTKZLRMVXKDHGLEBRVUX') allow_models = config.get('allow_socketio_models', '').strip().split(',') namespace = '/' + client_transfer_channel client_io = None web_socket_client = None msg_stack = [] model_env = None if getattr(server, 'main_thread_id') \ == threading.currentThread().ident:
def receive_train(self): ''' 接车, 开始排列进路, 进路开始排了才是 :return: ''' location = self.get_location_spell() data = self.get_plan_data() if data: if data["instructs"] == []: try: # 添加日志 log_record = self.env[ "metro_park_dispatch.train_in_out_log"].create([{ "type": "out_plan", "train_dev": self.train_id.train.id, "operation": "收车计划勾计划为空:{out_instructs}".format( out_instructs=str(data["instructs"])) }]) data["log_id"] = log_record.id except Exception as error: _logger.info("log error {error}".format(error=error)) else: try: # 添加日志 log_record = self.env[ "metro_park_dispatch.train_in_out_log"].create([{ "type": "out_plan", "train_dev": self.train_id.train.id, "operation": "收车计划勾计划:{out_instructs}".format( out_instructs=str(data["instructs"])) }]) data["log_id"] = log_record.id except Exception as error: _logger.info("log error {error}".format(error=error)) self.state = 'preparing' try: # 添加日志 log_record = self.env[ "metro_park_dispatch.train_in_out_log"].create([{ "type": "out_plan", "train_dev": self.train_id.train.id, "operation": "推送收车计划:{start_rail}-{end_rail}到信号楼".format( start_rail=self.real_start_rail.no, end_rail=self.plan_back_rail.no) }]) data["log_id"] = log_record.id except Exception as error: _logger.info("log error {error}".format(error=error)) # 之前出现指令重复的情况,防止重复 cache = dict() instructs = data["instructs"] new_instructs = [] for instruct in instructs: if instruct["start_rail"] not in cache: new_instructs.append(instruct) cache[instruct["start_rail"]] = True data["instructs"] = new_instructs # 添加计划, 信号楼如果已经有了则只是更新信息 try: self._cr.commit() self.trigger_up_event( "funenc_socketio_server_msg", data={ "msg_type": "add_plan", "msg_data": [data], "location": location }, room="xing_hao_lou", callback_name="_send_add_plan_success_callback") except Exception as error: LogManage.put_log( content='推送添加收车计划失败{error}'.format(error=error), mode='train_plan_log') # 添加日志 excute_record = None try: # 这个必需要加,不然回调处理record回出错, # 回调应当是一直在等侍,但回调函数在另一线程 excute_record = self.env[ "metro_park_dispatch.train_in_out_log"].create([{ "type": "out_plan", "train_dev": self.train_id.train.id, "operation": "推送执行收车计划:{start_rail}-{end_rail}到信号楼".format( start_rail=self.real_start_rail.no, end_rail=self.plan_back_rail.no) }]) except Exception as error: _logger.info("log error {error}".format(error=error)) # 执行计划,发送给信号楼 try: # 这个必需要加,不然回调处理record回出错, # 回调应当是一直在等侍,但回调函数在另一线程 self._cr.commit() self.trigger_up_event( "funenc_socketio_server_msg", data={ "msg_type": "excute_plan", "location": location, "msg_data": { "id": self.id, "type": 'train_back', "location": location, "log_id": excute_record.id if excute_record else None } }, room="xing_hao_lou", callback_name="_send_execute_plan_success_callback") except Exception as error: _logger.info("log error {error}".format(error=error)) LogManage.put_log( content='推送执行收车计划失败{error}'.format(error=error), mode='train_plan_log')
def update_position(self, location, dev_no, rail_no): ''' 更新现车位置 :param location: :param dev_no: :param rail_no: :return: ''' run_train = self.search([("train.dev_no", "=", dev_no)]) if not run_train: # 没有就创建车辆 run_train = self.create_run_train(dev_no, run_train) # 为了防止车号被行调删除(出现过被正线行调强行删除的情况,真是坑),直接返回,使用联锁进行判断 if not rail_no: return run_train.id rail = self.env["metro_park_base.rails_sec"]\ .search([('no', '=', rail_no), ('location', '=', location)]) if rail: # 由于ATS B股可能会出错,所以这里避开 if rail.port and rail.port == "B": return run_train.id rail_type_back_sec_id = self.env.ref( "metro_park_base.rail_type_back_sec").id rail_type_out_sec_id = self.env.ref( "metro_park_base.rail_type_out_sec").id LogManage.put_log( content='车{name}到达出{position}, ATS记录,不改变位置'.format( name=run_train["train_no"], position=rail["no"]), mode='cur_train_position_change') # 到达迁出线时删除车号 if rail.rail_type.id == rail_type_back_sec_id \ or rail.rail_type.id == rail_type_out_sec_id: # 两个都给清除掉 run_train.cur_switch = False run_train.cur_rail = False # 提前提交,防止别的线程访问不到数据, 这里是因为socketio的这个机制是要等侍回调返回, # 而别的地方使用的是另外的cursor,所以会访问不到数据 self._cr.commit() run_train.notify_train_position_changed(rail.location.alias) # 通知前端位置发生变化, 清除了无法知道位置 LogManage.put_log( content='车{name}到达{position}, 清除位置, ATS'.format( name=run_train["train_no"], position=rail["no"]), mode='cur_train_position_change') else: # 只有在转换轨道的时候才更新位置 rail_type_exchange_id = \ self.env.ref("metro_park_base.rail_type_exchange").id if rail.rail_type.id == rail_type_exchange_id: # 有可能联锁占压判断以后,它发又车还原回去了,所以只能车原来在转换轨道的时候发送 # 出去的时候无所谓,出去全部由mdias判断, 转换轨不设置车位置,所以车辆的当状轨道 # 和道岔只能为空 if not (run_train.cur_rail and run_train.cur_switch): run_train.cur_rail = rail.id run_train.cur_switch = False # 到达转换轨 LogManage.put_log( content='车{name}到达{position}, 到达转换轨-ATS'.format( name=run_train["train_no"], position=rail["no"]), mode='cur_train_position_change') # 提前提交,防止别的线程访问不到数据 self._cr.commit() # 通知前端位置发生变化 run_train.notify_train_position_changed() else: LogManage.put_log( content='车{name}到达{position}, 但原车已经在在场内-ATS'. format(name=run_train["train_no"], position=rail["no"]), mode='cur_train_position_change') else: # 记录, 用于联锁推断不出来的时候使用ats来更正 run_train.ats_position = rail_no return run_train.id
import threading from functools import partial from queue import Queue from odoo.addons.odoo_operation_log.model_extend import LogManage from sanic import Sanic try: from socketio import AsyncServer except: pass from odoo import models, api, tools from odoo.http import root from odoo.service.server import server from odoo.tools import config LogManage.register_type('socketio_log', "socketio日志") # 过滤socketio日志 logging.getLogger('socketio').setLevel(logging.WARNING) logging.getLogger('engineio').setLevel(logging.WARNING) _logger = logging.getLogger(__name__) message_queue = Queue() sio = None msg_stack = [] model_env = None async def execute_msg(msg): '''
# -*- coding: utf-8 -*- from odoo import models, fields, api, exceptions import pendulum import logging _logger = logging.getLogger(__name__) from odoo.addons.odoo_operation_log.model_extend import LogManage LogManage.register_type('plan_operation_log', "信号楼日志") class DayRunPlan(models.Model): ''' 运行计划, 这里只包括计划运营的内容 ''' _name = 'metro_park_dispatch.day_run_plan' _description = '运行计划' _rec_name = 'train' _track_log = True # 指定现车 train = fields.Many2one( string='车辆', comodel_name='metro_park_dispatch.cur_train_manage') date = fields.Date(string="日期") train_back_plan = fields.Many2one( string='收车作业', comodel_name='metro_park_dispatch.train_back_plan')
# -*- coding: utf-8 -*- from odoo import models, fields, api, exceptions import pendulum import logging import datetime import threading from queue import Queue from . import utility from odoo.addons.odoo_operation_log.model_extend import LogManage LogManage.register_type('train_plan_log', '计划日志') _logger = logging.getLogger(__name__) TRAIN_BACK_STATES = [("unpublish", "未发布"), ('preparing', '待接车'), ('executing', '执行中'), ('finished', '已完成'), ('canceled', '已取消')] class TrainBackPlan(models.Model): ''' 收车计划, 收车是从转换轨2收车 ''' _name = 'metro_park_dispatch.train_back_plan' _description = '收车计划' _rec_name = 'plan_train_no' _track_log = True day_plan_id = fields.Many2one( string='日计划', comodel_name='metro_park_dispatch.day_run_plan')
# -*- coding: utf-8 -*- import logging import pendulum from odoo import models, fields, api, exceptions from odoo.addons.odoo_operation_log.model_extend import LogManage from odoo.tools import config LogManage.register_type('synchronous_vehicles', '同步车辆') LogManage.register_type('manual_sync', '手动同步') LogManage.register_type('cur_train_position_change', '车辆位置跟踪') _logger = logging.getLogger(__name__) CUR_TRAIN_STATUS = [('fault', '故障'), ('repair', '检修'), ('detain', '扣车'), ('wait', '待命')] class CurTrainManage(models.Model): ''' 现车列表, 这里并没有车的轨迹跟踪 ''' _name = 'metro_park_dispatch.cur_train_manage' _rec_name = 'train_name' _description = '现车管理' _track_log = True train = fields.Many2one(string='车辆', comodel_name='metro_park_maintenance.train_dev')
# -*- coding: utf-8 -*- import re import time import datetime from odoo.addons.odoo_operation_log.model_extend import LogManage LogManage.register_type('login', '登录') from odoo import models, fields, api, exceptions class UserExtend(models.Model): ''' 扩展用户, 添加属性 ''' _inherit = 'res.users' _track_log = True locations = fields.Many2many(string="所属场段", comodel_name="metro_park_base.location", relation="user_location_rel", column1="user_id", column2="location_id") cur_location = fields.Many2one(string="当前场段", comodel_name="metro_park_base.location", domain="[('id', 'in', locations)]") today_continuous_error_count = fields.Integer('今日密码连续错误次数', default=0) no_login_minute = fields.Integer('当日限制登录分钟数', default=1) # 分钟数按当日失败进行翻倍累加 no_login_start_time = fields.Datetime('限制登录起始时间')
# -*- coding: utf-8 -*- import logging from odoo import models, api from odoo.exceptions import AccessDenied from odoo.models import SUPERUSER_ID import datetime _logger = logging.getLogger(__name__) # 直接odoo命名空间中去获取 from odoo.addons.odoo_operation_log.model_extend import LogManage LogManage.register_type('mobile_api_log', "调车终端日志") class MobileApi(models.AbstractModel): ''' 调车终端api ''' _name = 'metro_park_dispatch.mobile_api' @api.model def get_user_groups(self, user_id): ''' 取得当前用户的所有权限 :return: ''' user = self.env.user.browse(user_id) groups_ids = self.env['res.groups'].search([('id', 'in', user.groups_id.ids),