import struct import logging from django.db import transaction from vdes_utils.vdes_demod_data import VdesDemodData from vdes_utils.local_inspection_monitor import LocalInspectionMonitor from django.core.cache import cache logger = logging.getLogger(__name__) class VdesDataParsing: def __init__(self): self.hello = "VDES数据解析工具类" self.vdes_demod_data_parsing = VdesDemodData() #VDES解调数据解析类 self.local_inspection_monitor = LocalInspectionMonitor() #地检状态监控数据解析类 self.payload_fragments = [] # 用于存储长消息分片数据的数组 def parse_vdes_data(self, packet_data): from system.models import GpsStatus, HistorySession, Contacts, Content, MessageFlag from datetime import datetime import time #判断帧头是否符合琢研 # print(packet_data.hex()) if packet_data[:4].decode('ascii') == '####': # print("开始解析报文帧") packet_type = struct.unpack('>I', packet_data[4:8])[0] #获取报文类型 7为gps, 12为ais packet_lenth = struct.unpack('>I', packet_data[8:12])[0] packet_content = packet_data[12:] print('报文类型:', packet_type) if packet_type == 7: # print(f"GPS内容长度: {packet_lenth} 字节") data = self.get_gps_data(packet_content) utc_time = data.get('utc_time', 0) EPOCH_DIFF = 1577836800 # 2020-01-01 00:00:00 到 1970-01-01 00:00:00 的秒数差 try: if utc_time: # utc_time 是从 1980-01-06 开始的秒数 unix_timestamp = utc_time + EPOCH_DIFF utc_time_dt = datetime.fromtimestamp(unix_timestamp) else: utc_time_dt = datetime.now() except (ValueError, TypeError, OSError): utc_time_dt = datetime.now() # print(data) # print(utc_time_dt) #下面代码还应该根据正负判断前端应该显示南北纬东西经 GpsStatus.objects.create( utc_time=utc_time_dt, longitude=data.get('longitude', 0), latitude=data.get('latitude', 0), altitude=data.get('altitude', 0), used_satellites=data.get('used_sats', 10), gps_satellites=data.get('gps_sats', 6), beidou_satellites=data.get('beidou_sats', 4), gps_status=data.get('gps_status', 1), fix_mode=data.get('position_mode', 3), hdop=data.get('hdop', 1.2), speed=data.get('speed', 0), direction=data.get('direction', 0) ) elif packet_type == 8: print(f"地检内容长度: {packet_lenth} 字节") data = self.local_inspection_monitor.get_local_inspection_status_data(packet_content) cache.set("local_inspection_data", data, timeout=None) #存入redis缓存中,让另一个进程能获取 elif packet_type == 12: # print(packet_content) data = self.get_ais_vdes_data(packet_content) if data['message_type'] != 12: return # print(data) if data['demodulator_id'] <= 5: #0-5为VDES解调数据 # 检查demod_data的前四个字节是否为'#$%','#$%'为本系统判断数据(文本图片视频)类型的标识 if len(data['demod_data']) >= 4 and data['demod_data'][:4] == b'#$%': print("检测到特殊标识符'#$%',打印数据:", data['demod_data']) else: self.vdes_demod_data_parsing.main(data['demod_data']) # print(f"VDES内容长度: {packet_lenth} 字节") # print(f"VDES内容长度: {packet_lenth} 字节") # demod_data = self.get_vdes_demod_data(data['demod_data']) # print('VDES解调数据:', demod_data) # print("----------------------------") # time.sleep(0.1) # # 此处需要判断消息帧类型,卫星公告牌之类的---------------------------------------- # combined_data = self.vdes_data_processing(demod_data) # # 此处需要判断接收的消息数据格式---------------------------------------- # if combined_data is not None: # # print("完整消息:", combined_data) # # 保存为图片文件 # import os # from datetime import datetime # # 创建保存目录(如果不存在) # save_dir = 'E:\projects\wxwx\projects_job\VDES_Projects\报文模拟解析\地检设备模拟\\tcp_data' # os.makedirs(save_dir, exist_ok=True) # # 生成文件名(带时间戳) # timestamp = datetime.now().strftime('%Y%m%d_%H%M%S') # filename = os.path.join(save_dir, f'received_image_{timestamp}.jpg') # 假设是JPEG格式 # # 写入文件 # with open(filename, 'wb') as f: # f.write(combined_data) # print(f"图片已保存到: {filename}") # return # if combined_data is not None: # MMSI = demod_data['Dest_id'] # print(f"发送者MMSI: {MMSI}") # print(f"消息: {combined_data}") # # 查询或创建通讯录记录 # contact, created = Contacts.objects.get_or_create(MMSI=MMSI) # Id = contact.Id # contact, created = HistorySession.objects.get_or_create(FormId=Id, MMSI=MMSI) # content = Content.objects.create( # SendId=Id, # ReciverId=1, # Content="
"+combined_data+"
", # Type=0, # 0=html文本 # State=1, # 已发送 # NoCode=str(int(time.time())) , # 你可以放时间戳 # CreateDateUtc=datetime.now().strftime('%Y-%m-%d %H:%M:%S'), # ReadFlag=0, # SoundStatus=0 # ) # flag = MessageFlag.objects.first() # if flag: # flag.has_new_message = True # flag.save() else: #6/7为AIS解调数据 try: demod_data = self.get_ais_demod_data(data['demod_data']) # print(demod_data) # 处理并存储AIS解调数据 self.ais_data_processing(demod_data) except ValueError as e: print(f"跳过不支持的AIS消息: {e}") def get_gps_data(self, packet_content): # 解析前40字节的GPS信息 fields = struct.unpack('>QiiiHHHBBIII', packet_content[:40]) # 解析卫星状态数据 satellite_data = [] remaining_data = packet_content[40:] # 每6字节为一组卫星数据 for i in range(0, len(remaining_data), 6): satellite_bytes = remaining_data[i:i+6] if len(satellite_bytes) < 6: break # 不足6字节的数据丢弃 # 解包卫星数据 sat_type, prn, snr, elevation, azimuth = struct.unpack('>BBBBH', satellite_bytes) satellite_data.append({ 'satellite_type': sat_type, # 卫星类型 'prn': prn, # 卫星PRN码 'snr': snr, # 卫星信噪比(dBHz) 'elevation': elevation, # 卫星仰角(0-90度) 'azimuth': azimuth # 卫星方位角(0-359度) }) res = {} # 将度分格式转换为十进制度格式 dm_to_decimal = lambda raw: (1 if raw >= 0 else -1) * ( int(abs(raw) // 10000000) + (abs(raw) % 10000000) / 10000000 / 60 * 100 ) res['utc_time'] = fields[0] # UTC时间 (8字节) res['longitude'] = round(dm_to_decimal(fields[1]), 7) res['latitude'] = round(dm_to_decimal(fields[2]), 7) res['altitude'] = fields[3]/10 # 海拔高度 (4字节,50米) res['used_sats'] = fields[4] # 定位使用的卫星数 (2字节) res['gps_sats'] = fields[5] # GPS卫星数 (2字节) res['beidou_sats'] = fields[6] # 北斗卫星数 (2字节) res['gps_status'] = fields[7] # GPS状态 (1字节,单点定位) res['position_mode'] = fields[8] # 定位模式 (1字节,3D定位) res['hdop'] = fields[9]/10 # 水平精度因子 (4字节,25.5米) res['speed'] = fields[10]/100 # 运动速度 (4字节,10.24节) res['direction'] = fields[11]/100 # 运动方向 (4字节,359度) res['satellite_data'] = satellite_data # 卫星状态数据列表 return res def get_ais_vdes_data(self, packet_content): res ={} demod_packet = packet_content[:11] demod_data = packet_content[11:] fields = struct.unpack('>BBBHHHH', demod_packet) res['message_type'] = fields[0] res['satellite_id'] = fields[1] res['demodulator_id'] = fields[2] res['demod_slot'] = fields[3] res['receive_delay'] = fields[4] snr_packed = fields[5] res['frequency_bias'] = fields[6] # 解析SNR snr_int = (snr_packed >> 8) & 0xFF snr_frac = snr_packed & 0xFF res['snr'] = snr_int + snr_frac / 100.0 res['demod_data'] = demod_data # bit_string = "".join(f"{byte:08b}" for byte in demod_data) # print(bit_string) # print(demod_data.hex()) return res def get_ais_demod_data(self, bit_string): # 如果输入是字节类型,先转换为二进制字符串 if isinstance(bit_string, bytes): bit_string = ''.join(format(byte, '08b') for byte in bit_string) pos = 0 decoded = {} decoded['message_id'] = int(bit_string[pos:pos+6], 2) # 消息ID (6 bits) # 检查message_id是否为1、2或3,如果不是则抛出异常 (当前程序只解析了位置报告,还不支持其他ais报文) if decoded['message_id'] not in [1, 2, 3]: raise ValueError(f"Unsupported message_id: {decoded['message_id']}") pos += 6 decoded['repeat_indicator'] = int(bit_string[pos:pos+2], 2) # 转发指示符 (2 bits) pos += 2 decoded['user_id'] = int(bit_string[pos:pos+30], 2) # 用户ID/MMSI (30 bits) pos += 30 decoded['navigation_status'] = int(bit_string[pos:pos+4], 2) # 导航状态 (4 bits) pos += 4 # 旋转速率 (8位有符号整数) rot_value = int(bit_string[pos:pos+8], 2) if rot_value > 127: rot_value -= 256 decoded['rot'] = rot_value pos += 8 decoded['sog'] = int(bit_string[pos:pos+10], 2) # 地面航速 (10 bits) pos += 10 decoded['position_accuracy'] = int(bit_string[pos], 2) # 位置准确度 (1 bit) pos += 1 # 经度 (28位) longitude_value = int(bit_string[pos:pos+28], 2) decoded['longitude'] = longitude_value / (60 * 10000) # 转换为度 pos += 28 # 纬度 (27位) latitude_value = int(bit_string[pos:pos+27], 2) decoded['latitude'] = latitude_value / (60 * 10000) # 转换为度 pos += 27 decoded['cog'] = int(bit_string[pos:pos+12], 2) # 地面航线 (12 bits) pos += 12 decoded['true_heading'] = int(bit_string[pos:pos+9], 2) # 实际航向 (9 bits) pos += 9 decoded['timestamp'] = int(bit_string[pos:pos+6], 2) # 时戳 (6 bits) pos += 6 decoded['special_maneuver'] = int(bit_string[pos:pos+2], 2) # 特定操纵指示符 (2 bits) pos += 2 decoded['spare'] = int(bit_string[pos:pos+3], 2) # 备用 (3 bits) pos += 3 decoded['raim_flag'] = int(bit_string[pos], 2) # RAIM标志 (1 bit) pos += 1 decoded['comm_state'] = int(bit_string[pos:pos+19], 2) # 通信状态 (19 bits) return decoded def get_vdes_demod_data(self, packed_data): """解码VDES解调信息字段""" header_size = struct.calcsize('>B H I B B I H') header = packed_data[:header_size] Type, payload_size, source_id, satellite_id, session_id, dest_id, fragment_num = \ struct.unpack('>B H I B B I H', header) # 计算负载字节长度进去截取,去除帧尾 payload = packed_data[header_size:header_size+payload_size - 12] # 将UTF-8编码的二进制payload解码为字符串 try: decoded_payload = payload.decode('utf-8') except UnicodeDecodeError: decoded_payload = payload # 如果解码失败,保留原始二进制数据 return { 'Type': Type, 'Payload_size': payload_size, 'Source_id': source_id, 'Satellite_id': satellite_id, 'Session_id': session_id, 'Dest_id': dest_id, 'Fragment_num': fragment_num, 'Payload': decoded_payload # 返回解码后的字符串 } def ais_data_processing(self, demod_data): from system.models import ShipLatestStatus from datetime import datetime # 转换timestamp为datetime对象 timestamp = demod_data.get('timestamp', 0) try: timestamp_dt = datetime.fromtimestamp(timestamp) if timestamp else datetime.now() except (ValueError, TypeError): timestamp_dt = datetime.now() ship_data = { 'mmsi': str(demod_data.get('user_id', '000000000')), 'latitude': demod_data.get('latitude', 0), 'longitude': demod_data.get('longitude', 0), 'speed': demod_data.get('sog', 0), 'course': demod_data.get('cog', 0), 'heading': demod_data.get('true_heading', 0), 'nav_status': demod_data.get('navigation_status', 0), 'timestamp': timestamp_dt, # 使用转换后的datetime对象 'ship_name': '未知', 'ship_type': 0, 'draught': 0, 'destination': '未知' } with transaction.atomic(): # 尝试获取已有记录 ship, created = ShipLatestStatus.objects.get_or_create( mmsi=ship_data['mmsi'], defaults=ship_data ) if not created: # 更新已有记录 for field, value in ship_data.items(): setattr(ship, field, value) ship.save() # def vdes_data_processing(self, demod_data): # """处理VDES分片数据""" # fragment_num = demod_data['Fragment_num'] # print(self.payload_fragments) # if demod_data['Type'] == 30: # 开始分片 # self.payload_fragments = [] # 清空数组 # elif demod_data['Type'] == 31: # 数据分片 # # 确保数组足够大以容纳当前分片 # while len(self.payload_fragments) <= fragment_num: # self.payload_fragments.append(None) # self.payload_fragments[fragment_num] = demod_data['Payload'] # elif demod_data['Type'] == 32: # 结束分片 # # 拼接所有有效分片 # combined_payload = ''.join([p for p in self.payload_fragments if p is not None]) # return combined_payload # return None # 如果不是结束分片,返回None def vdes_data_processing(self, demod_data): """处理VDES分片数据""" fragment_num = demod_data['Fragment_num'] # print(self.payload_fragments) if demod_data['Type'] == 30: # 开始分片 print("------------------开始——————————————————————————————————————————————") self.payload_fragments = [] # 清空数组 self.payload_fragments.append(demod_data['Payload']) elif demod_data['Type'] == 31: # 数据分片 self.payload_fragments.append(demod_data['Payload']) elif demod_data['Type'] == 32: # 结束分片 # 拼接所有有效分片 print("------------------结束——————————————————————————————————————————————") self.payload_fragments.append(demod_data['Payload']) combined_payload = b''.join([p for p in self.payload_fragments if p is not None]) print(len(self.payload_fragments)) return combined_payload return None # 如果不是结束分片,返回None