# -*- coding: UTF-8 -*- import zlib from xmlrpc.client import Boolean from drivers.baseSerail import BaseSerial import binascii import drivers.baseSerail as baseSerail # 包头码(包引导码) PHeader = { "TV_Debug": 0xAA, "TV_Return": 0xAB, "TV_Panel_Debug": 0xAC, "TV_Panel_Return": 0xAD, "TV_Debug_Other": 0xAE, "TV_Other_Return": 0xAF, } # 命令结果码; RCode = { "RC_OK": 0x0A, # 命令执行通过; "RC_ERR": 0x0E, # 命令错误或无法执行; "RC_LOSE": 0x0F, # 命令丢失(链路层出错); } # 命令的封装与解析; class TvParse(): def __init__(self): # 头引导码(默认1字节,0x86命令二字节); self.Header = [PHeader['TV_Debug']] # 包长(默认1字节,0x86长度二字节); self.Length = [0x00] # 命令码,1字节(int类型); self.Command = [0x00] # 子命令参数; self.SubCommand = [] # 数据,bytearray格式; self.Data = [] # crch,1字节; self.CRCH = 0xFF # crcl,1字节; self.CRCL = 0xFF # 是否是特殊命令; self.FEFlag = False # 是否是多参数命令;(同一个Command,有多个含义的参数) self.isMultipleParams = False # 正确执行后的结果值,bytearray格式的二维数组; self.successData = [] def parseCommand(self, head: int, command: list[int], subCommand: list[int] = [], data: bytearray = bytearray(), isMultipleParams=False, FEFlag=False): self.Header[0] = head self.Command = command self.SubCommand = subCommand self.Data = data self.FEFlag = FEFlag self.isMultipleParams = isMultipleParams if FEFlag: self.Header.append(0xFE) # 注意:4 = crch + crcl + lenh + lenl length = 4 + self.Header.__len__() + self.Data.__len__() + self.Command.__len__() + self.SubCommand.__len__() # 高字节; self.Length[0] = length >> 8 # 低字节; self.Length.append(length & 0xFF) else: # 注意:3 = crch + crcl + len self.Length[0] = 3 + self.Header.__len__() + self.Data.__len__() + self.Command.__len__() + self.SubCommand.__len__() # 生成package; package = bytearray(self.Header + self.Length + self.Command + self.SubCommand) + self.Data self.calculateCRC(package) # 形成最终的命令; package = bytearray(self.Header + self.Length + self.Command + self.SubCommand) + self.Data + bytearray([self.CRCH, self.CRCL]) # 打印最终命令的十六进制形式,每两个字符之间加一个空格 # hex_string = ' '.join(f'{byte:02X}' for byte in package) # print("最终命令的十六进制形式:", hex_string) return package def calculateCRC(self, data: bytearray): crc = baseSerail.crc16(data, data.__len__()) # 高字节; self.CRCH = crc >> 8 # 低字节; self.CRCL = crc & 0xFF def parseCommandFc(self, head: int, command: list[int], subCommand: list[int] = [], data: bytearray = bytearray(), isMultipleParams=False): self.Header[0] = head self.Command = command self.SubCommand = subCommand self.Data = data self.isMultipleParams = isMultipleParams self.Length[0] = 3 + self.Header.__len__() + self.Data.__len__() + self.Command.__len__() + self.SubCommand.__len__() package = bytearray(self.Header + self.Length + self.Command + self.SubCommand) + self.Data self.calculateCRC(package) package = bytearray(self.Header + self.Length + self.Command + self.SubCommand) + self.Data + bytearray([self.CRCH]) + bytearray([self.CRCL]) return package # 成功返回:(AB 08 FC 08 EC 80 CR1 CR2) # 失败返回:(AB 08 FC 08 EC 00 CR1 CR2) def parseResultFc(self,data): data = bytearray(data) return False def parseResult(self, data): data = bytearray(data) if data.__len__() < 5: return False retCode = 0 if self.Header[0] == PHeader['TV_Debug']: retCode = PHeader['TV_Return'] elif self.Header[0] == PHeader['TV_Panel_Debug']: retCode = PHeader['TV_Panel_Return'] elif self.Header[0] == PHeader['TV_Debug_Other']: retCode = PHeader['TV_Other_Return'] if retCode != data[0]: return False package = [] tooken_len = 0 return True def parseString(self, data): data = bytearray(data) if data.__len__() < 5: return False retCode = 0 if self.Header[0] == PHeader['TV_Debug']: retCode = PHeader['TV_Return'] elif self.Header[0] == PHeader['TV_Panel_Debug']: retCode = PHeader['TV_Panel_Return'] elif self.Header[0] == PHeader['TV_Debug_Other']: retCode = PHeader['TV_Other_Return'] if retCode != data[0]: return False package = [] tooken_len = 0 while True: if tooken_len >= data.__len__(): break if self.FEFlag: package_len = data[tooken_len + 1] << 8 + data[tooken_len + 2] else: package_len = data[tooken_len + 1] package = data[tooken_len:tooken_len + package_len] if package[0] != retCode: print('Incorrect package head!\n') return False crc = crc16(package, package_len - 2) CRCH = crc >> 8 CRCL = crc & 0xFF if CRCH != package[-2] and CRCL != package[-1]: return False if tooken_len == 0: if package[2] != RCode['RC_OK']: return False else: if self.Command[0] == 0xFC: pass else: if package[2] - 1 != self.Command[0]: return False if package[2] == 0xFE: self.successData.append(package[5:-2]) else: self.successData.append(package[3:-2]) tooken_len += package_len # print('successData:', binascii.b2a_hex(bytearray(self.Command)), self.successData) return self.successData class tvSerial(BaseSerial): def __init__(self): BaseSerial.__init__(self) def __del__(self): self.close() def checkport(self): if not self.ser.is_open: self.reOpen() return self.ser.is_open def sendcmd(self, cmd: list[int]): self.write(bytearray(cmd)) return self.read() '''协议模式发送命令''' def sendcmdEx(self, head: int, command: list[int], subCommand: list[int] = [], data: bytearray = bytearray(), FEFlag: Boolean = False, returnParam: Boolean = False): cmd = TvParse() package = cmd.parseCommand(head, command, subCommand, data, returnParam, FEFlag) if self.write(package): package = self.read() return cmd.parseResult(package) return False def sendcmdEx_string_return(self, head: int, command: list[int], subCommand: list[int] = [], data: bytearray = bytearray(), FEFlag: Boolean = False, returnParam: Boolean = False): cmd = TvParse() package = cmd.parseCommand(head, command, subCommand, data, returnParam, FEFlag) if self.write(package): package = self.read() return cmd.parseString(package) return "" def sendcmdFc(self, head: int, command: list[int], subCommand: list[int] = [], data: bytearray = bytearray(), FEFlag: Boolean = False, returnParam: Boolean = False): cmd = TvParse() package = cmd.parseCommand(head, command, subCommand, data, returnParam, FEFlag) if self.write(package): package = self.read() return cmd.parseResult(package) return False def gen_zzip(selfe,gm_file,save_file): with open(gm_file, 'rb') as f: data = f.read() compressed_data = zlib.compress(data) with open(save_file, 'wb') as f: f.write(compressed_data) '''发送pattern''' def send_parttern(self, rgb: list[int]): cmd = TvParse() package = cmd.parseCommand(0xAA, [0x28], [], bytearray(rgb)) if self.write(package): package = self.read() return cmd.parseResult(package) return False # '''发送10bit-pattern''' # def send_10_bit_parttern(self, rgb: list[int]): # cmd = MokaParse() # rgb16 = convert_rgb16(rgb) # package = cmd.parseCommandFc(0xAA, [0xFC,0x07,0x06], [0xEC,0x01,0x86,0x00], rgb16) # if self.write(package): # package = self.read() # return cmd.parseResult(package) # return False '''发送12bit-pattern''' def send_12_bit_parttern(self, rgb: list[int]): cmd = TvParse() rgb16 = baseSerail.convert_rgb16(rgb) package = cmd.parseCommand(0xAA, [0x28], [], rgb16) if self.write(package): package = self.read() return cmd.parseResult(package) return False '''发送gamma文件''' def send_gamma(self, file_path: str): fp = open(file_path, 'rb') cmd = TvParse() data = fp.read() # 生成命令包 package = cmd.parseCommand(0xAA, [0xE9], [0x02], data, False, True) # 将命令包写入临时文件 if self.write(package): package = self.read() print(cmd.parseResult(package)) return cmd.parseResult(package) '''激活gamma文件''' def send_gamma_active(self, ini_file: str): ini_fp = open(ini_file, 'rb') ind_data = ini_fp.read() ini_fp.close() # 计算crc crc = crc16(ind_data, ind_data.__len__()) CRC_GM = [crc >> 8, crc & 0xFF] cmd = TvParse() package = cmd.parseCommand(0xAA, [0x99], [0x06], bytearray(CRC_GM)) if self.write(package): package = self.read() print("---------------------") print(cmd.parseResult(package)) return cmd.parseResult(package) '''激活gamma文件-12bit版本''' def send_gamma_active_12bit(self, ini_file: str): ini_fp = open(ini_file, 'rb') ind_data = ini_fp.read() ini_fp.close() # 计算crc crc = baseSerail.crc16(ind_data, ind_data.__len__()) CRC_GM = [crc >> 8, crc & 0xFF] cmd = TvParse() package = cmd.parseCommandFc(0xAA, [0xFC,0x07], [0x0E], bytearray(CRC_GM)) if self.write(package): package = self.read() print("---------------------") print(cmd.parseResult(package)) return cmd.parseResult(package) '''进工厂模式''' def enterFactory(self): return self.sendcmdEx(0xAA, [0x10], [0x01]) '''白平衡初始化''' def initWhiteBalance(self): return self.sendcmdEx(0xAA, [0x16], [0x01]) '''关闭localdimming''' def closeLocaldimming(self): return self.sendcmdEx(0xAA, [0x9F,0x07], [0x00]) '''打开内置pattern''' def openBuiltInPattern(self): return self.sendcmdEx(0xAA, [0x27], [0x01]) '''关闭内置pattern''' def closeBuiltInPattern(self): return self.sendcmdEx(0xAA, [0x27], [0x00]) '''切换标准色温''' def switchStdColorTemperature(self): return self.sendcmdEx(0xAA, [0x31], [0x01]) '''切换冷色温''' def switchColdColorTemperature(self): return self.sendcmdEx(0xAA, [0x31], [0x02]) '''切换暖色温''' def switchWarmColorTemperature(self): return self.sendcmdEx(0xAA, [0x31], [0x03]) '''切换暖2色温''' def switchWarm2ColorTemperature(self): return self.sendcmdEx(0xAA, [0x31], [0x04]) '''初始化gamma''' def initGamma(self): return self.sendcmdEx(0xAA, [0x9F,0x09], [0x01]) '''进老化模式''' def enterAgingMode(self): return self.sendcmdEx(0xAA, [0x13], [0x01]) '''退出老化模式''' def exitAgingMode(self): return self.sendcmdEx(0xAA, [0x13], [0x00]) '''软件版本查询''' def sendSoftwareVersionQuery(self): return self.sendcmdEx_string_return(0xAA, [0x57], [0x00]) '''PID查询''' def sendPIDQuery(self): return self.sendcmdEx_string_return(0xAA, [0x84], [0x00]) def switchHDMI1(self): """切换到HDMI1信源""" return self.sendcmdEx(0xAA, [0x25], [0x01]) def switchHDMI2(self): """切换到HDMI2信源""" return self.sendcmdEx(0xAA, [0x25], [0x02]) def switchHDMI3(self): """切换到HDMI3信源""" return self.sendcmdEx(0xAA, [0x25], [0x03]) def switchVGA(self): """切换到VGA信源""" return self.sendcmdEx(0xAA, [0x24], [0x01]) def switchAV1(self): """切换到AV1信源""" return self.sendcmdEx(0xAA, [0x22], [0x01]) def switchAV2(self): """切换到AV2信源""" return self.sendcmdEx(0xAA, [0x22], [0x02]) def switchAV3(self): """切换到AV3信源""" return self.sendcmdEx(0xAA, [0x22], [0x03]) def switchDisplayMode(self, mode="natural"): """切换图像预设模式 """ if mode == "natural": return self.sendcmdEx(0xAA, [0x30], [0x01]) elif mode == "soft": return self.sendcmdEx(0xAA, [0x30], [0x02]) elif mode == "bright": return self.sendcmdEx(0xAA, [0x30], [0x03]) elif mode == "personal": return self.sendcmdEx(0xAA, [0x30], [0x04]) elif mode == "cinematic": return self.sendcmdEx(0xAA, [0x30], [0x05]) else: raise ValueError("Invalid display mode. Use 'natural', 'soft', 'bright', 'personal', or 'cinematic'.") if __name__ == "__main__": pass