Commit e424bd99 authored by 时海鑫's avatar 时海鑫

Merge branch 'dev' into 'master'

test4

See merge request !18
parents aef499f1 372d8daf
package com.tyw.colortest.tools;
import java.util.Arrays;
public class ProtocolHelper {
/**
* 【最终正确版】构建 "MFD backlight mode" (0x83) 的指令帧。
* 此版本基于已验证的实际数据编写,正确实现了此命令独特的Length定义和帧结构。
*
* @param aliveCount 那个0-15循环的计数器,它是data载荷的一部分
* @param backlightOn 背光是否开启 (true为开, false为关)
* @param brightness 背光亮度 (0-100)
* @return 完整的、正确的10字节指令数组。
*/
public static byte[] buildMfdBacklightModeCommand ( int brightness) {
// --- 1. 根据验证过的数据,定义完整的10字节帧结构 ---
final int TOTAL_FRAME_LENGTH = 10;
byte[] commandFrame = new byte[TOTAL_FRAME_LENGTH];
// Byte 0: 命令ID
commandFrame[0] = (byte) 0x83;
// Byte 1: Length (值为整个帧的总长度)
commandFrame[1] = (byte) TOTAL_FRAME_LENGTH; // 即 0x0A
// Byte 2: 通用协议中定义的、固定的Alive Counter预留位
commandFrame[2] = 0x00;
// Bytes 3-8: 0x83命令专属的6字节data载荷
commandFrame[3] =0x00; // data[0]: 0-15循环的Alive_Count
commandFrame[4] = 0x00; // data[1]: E2E状态
commandFrame[5] = 0x01;
commandFrame[6] = (byte) brightness; // data[3]: 亮度
commandFrame[7] = 0x00; // data[4]: reserved
// 注意:根据验证数据,此预留位是0x10,而不是文档中的0x00
commandFrame[8] = 0x10; // data[5]: reserved
// --- 2. 计算CRC ---
// 对除最后一个CRC字节外的所有字节进行计算
byte[] dataForCrc = Arrays.copyOf(commandFrame, TOTAL_FRAME_LENGTH - 1);
byte crc8 = calculateCrc8(dataForCrc);
// --- 3. 将CRC值放入最后一个字节 ---
commandFrame[9] = crc8;
return commandFrame;
}
/**
* 根据协议文档,为 "CSC startup done" (0x80) 命令构建完整的指令帧。
* @param brightness Backlight brightness (0-100)
* @return 完整的指令字节数组,可以直接在I2C总线上发送 (不含地址)。
*/
public static byte[] buildCscStartupDoneCommand( int brightness) {
// --- 1. 定义固定参数和载荷长度 ---
final byte CMD_ID = (byte) 0x80;
final byte ALIVE_COUNTER = 0x00;
final int DATA_PAYLOAD_LENGTH = 8; // data[n] 的长度为8字节
// --- 2. 计算Length字段 ---
// Length = Alive Counter的长度(1) + data的长度(8) = 9
final byte LENGTH = 0x0B;
// --- 3. 构建 data[n] 载荷 ---
byte[] dataPayload = new byte[DATA_PAYLOAD_LENGTH];
dataPayload[0] = 0x01; // Byte1: CSC startup result
dataPayload[1] = 0x01; // Byte2: MFD backlight work mode
dataPayload[2] = (byte) brightness; // Byte3: Backlight brightness
dataPayload[3] = 0x00; // Byte4: Rear screen info
dataPayload[4] = 0x00; // Byte5: reserved
dataPayload[5] = 0x00; // Byte6: reserved
dataPayload[6] = 0x00; // Byte7: reserved
dataPayload[7] = 0x00; // Byte8: reserved
// --- 4. 准备用于CRC计算的数据 ---
// 范围: CMD_ID, Length, Alive Counter, data[n]
byte[] dataForCrc = new byte[1 + 1 + 1 + DATA_PAYLOAD_LENGTH]; // 总共11字节
dataForCrc[0] = CMD_ID;
dataForCrc[1] = LENGTH;
dataForCrc[2] = ALIVE_COUNTER;
System.arraycopy(dataPayload, 0, dataForCrc, 3, DATA_PAYLOAD_LENGTH);
// --- 5. 计算CRC ---
byte crc8 = calculateCrc8(dataForCrc);
// --- 6. 组装最终的完整指令帧 ---
// 顺序: CMD_ID, Length, Alive Counter, data[n], Crc8
byte[] commandFrame = new byte[1 + 1 + 1 + DATA_PAYLOAD_LENGTH + 1]; // 总共12字节
System.arraycopy(dataForCrc, 0, commandFrame, 0, dataForCrc.length);
commandFrame[commandFrame.length - 1] = crc8; // 将CRC放在最后
return commandFrame;
}
/**
* 【全新】构建 "获取版本号" (0x81) 的指令帧。
*
* @return 用于获取版本号的完整指令字节数组。
*/
public static byte[] buildGetVersionCommand_IncorrectLogic(byte pay) {
// --- 1. 定义指令参数 ---
final byte CMD_ID = (byte) 0x81;
final byte ALIVE_COUNTER = 0x00;
final byte[] dataPayload = { pay }; // 请求版本号
// --- 2. 【按字面错误理解】计算Length字段 ---
// 注释: Length = 包含Length+CMD ID+Data(n)+ALIVE_Count+CRC8
// 我们计算这些字段的数量总和:
// Length字段(1) + CMD_ID(1) + Alive Counter(1) + data(1) + Crc8字段(1) = 5
final byte INCORRECT_LENGTH = 5; // 即 0x05
// --- 3. 准备用于CRC计算的数据 ---
// CRC计算范围依然是 CMD_ID, Length, Alive Counter, data[n]
// 我们将使用上面计算出的 INCORRECT_LENGTH (0x05) 作为Length的值参与计算
byte[] dataForCrc = new byte[] {
CMD_ID, // 0x81
INCORRECT_LENGTH, // 0x05
ALIVE_COUNTER, // 0x00
dataPayload[0] // 0x48
};
// --- 4. 计算CRC ---
byte crc8 = calculateCrc8(dataForCrc);
// --- 5. 组装最终的完整指令帧 ---
// 顺序: CMD_ID, Length, Alive Counter, data[n], Crc8 (共5字节)
byte[] commandFrame = new byte[] {
CMD_ID,
INCORRECT_LENGTH,
ALIVE_COUNTER,
dataPayload[0],
crc8
};
return commandFrame;
}
/**
* 根据提供的C代码翻译的CRC-8计算方法。
* 算法参数: Poly=0x1D, Init=0xFF
*/
public static byte calculateCrc8(byte[] data) {
int crcValue = 0xFF;
final int crcPoly = 0x1D;
for (byte b : data) {
crcValue ^= (b & 0xFF);
for (int i = 0; i < 8; i++) {
if ((crcValue & 0x80) != 0) {
crcValue = ((crcValue << 1) ^ crcPoly) & 0xFF;
} else {
crcValue = (crcValue << 1) & 0xFF;
}
}
}
return (byte) crcValue;
}
// 辅助方法:将字节数组转换为十六进制字符串以便查看
public static String bytesToHexString(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("0x%02X ", b));
}
return sb.toString().trim();
}
}
\ No newline at end of file
package com.tyw.colortest.tools;
import android.content.Context;
import android.content.SharedPreferences;
import android.util.Log;
import com.tyw.colortest.bean.UsbConfig;
import com.tyw.colortest.bean.UsbConfigXlsxBean;
import com.tyw.colortest.constance.AppConfig; // 【新增】import
import com.tyw.colortest.constance.LHStyle; // 【新增】import
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class SpUtils {
public static final String KEY_APP_CONFIG = "app_config";
public static final String KEY_LH_STYLE = "lh_style";
public static final String KEY_LH_TIME = "lh_time";
private static final String prefsName="my_preferences";
private static final String PREFS_APP_GENERAL = "AppGeneralPrefs";
// --- 旧的 Key ---
public static final String KEY_PRODUCTION_VERSION = "version";
public static final String KEY_PRODUCTION_ID = "报文ID";
// --- 【新增】为每个图片目录定义的 Key ---
public static final String KEY_SPEC_PATHS = "spec_image_paths";
public static final String KEY_LAOHUA_PATHS = "laohua_image_paths";
public static final String KEY_JIANCETAI_PATHS = "jiancetai_image_paths";
public static final String KEY_GAMMA_PATHS = "gamma_image_paths";
public static final String KEY_FLIKER_PATHS = "fliker_image_paths";
public static final String KEY_BW_PATHS = "black_white_image_paths";
public static final String KEY_COLOR_PATHS = "color_image_paths";
public static final String KEY_CR_PATHS = "cr_image_paths";
public static final String KEY_TIEHE_PATHS = "tiehe_image_paths"; // 【新增】
private static SharedPreferences getPrefs(Context context) {
return context.getSharedPreferences(PREFS_APP_GENERAL, Context.MODE_PRIVATE);
}
// --- 旧的方法 (保持不变) ---
public static void setProductionVersion(Context context, String version) {
getPrefs(context).edit().putString(KEY_PRODUCTION_VERSION, version).apply();
}
public static String getProductionVersion(Context context, String defaultValue) {
return getPrefs(context).getString(KEY_PRODUCTION_VERSION, defaultValue);
}
public static void setProductionID(Context context, String version) {
getPrefs(context).edit().putString(KEY_PRODUCTION_ID, version).apply();
}
public static String getProductionID(Context context, String defaultValue) {
return getPrefs(context).getString(KEY_PRODUCTION_ID, defaultValue);
}
public static void setAppConfig(Context context, AppConfig config) {
if (config == null) return;
getPrefs(context).edit().putString(KEY_APP_CONFIG, config.name()).apply();
}
/**
* 读取 AppConfig (工装类型)
* @param defaultValue 如果SP中没有,则返回此默认值
*/
public static AppConfig getAppConfig(Context context, AppConfig defaultValue) {
String name = getPrefs(context).getString(KEY_APP_CONFIG, null);
if (name == null) {
return defaultValue;
}
try {
return AppConfig.valueOf(name);
} catch (IllegalArgumentException e) {
// 如果SP中存的值无效 (例如,枚举名已更改),返回默认值
return defaultValue;
}
}
/**
* 保存 LHStyle (老化类型)
*/
public static void setLhStyle(Context context, LHStyle style) {
if (style == null) return;
getPrefs(context).edit().putString(KEY_LH_STYLE, style.name()).apply();
}
/**
* 读取 LHStyle (老化类型)
*/
public static LHStyle getLhStyle(Context context, LHStyle defaultValue) {
String name = getPrefs(context).getString(KEY_LH_STYLE, null);
if (name == null) {
return defaultValue;
}
try {
return LHStyle.valueOf(name);
} catch (IllegalArgumentException e) {
return defaultValue;
}
}
/**
* 保存老化时间
*/
public static void setLhTime(Context context, int time) {
getPrefs(context).edit().putInt(KEY_LH_TIME, time).apply();
}
/**
* 读取老化时间
*/
public static int getLhTime(Context context, int defaultValue) {
return getPrefs(context).getInt(KEY_LH_TIME, defaultValue);
}
// --- 【新增】缓存 UsbConfig 对象的 Key ---
public static final String KEY_USB_CONFIG_CACHE = "usb_config_cache";
/**
* 【新增】将 UsbConfig 对象序列化为 JSON 字符串并保存到 SharedPreferences。
* @param context Context
* @param config 要缓存的 UsbConfig 对象。如果为 null,则会清除缓存。
*/
public static void saveUsbConfig(Context context, UsbConfigXlsxBean config) {
SharedPreferences.Editor editor = getPrefs(context).edit();
if (config == null) {
// 如果传入 null,则移除缓存
editor.remove(KEY_USB_CONFIG_CACHE).apply();
Log.d("SpUtils", "UsbConfig cache cleared.");
} else {
// 使用 Gson 将对象转换为 JSON 字符串
com.google.gson.Gson gson = new com.google.gson.Gson();
String jsonString = gson.toJson(config);
editor.putString(KEY_USB_CONFIG_CACHE, jsonString).apply();
Log.d("SpUtils", "UsbConfig cached successfully.");
}
}
/**
* 【新增】从 SharedPreferences 读取 JSON 字符串并反序列化为 UsbConfig 对象。
* @param context Context
* @return 返回缓存的 UsbConfig 对象。如果没有缓存或解析失败,则返回 null。
*/
public static UsbConfigXlsxBean getUsbConfig(Context context) {
String jsonString = getPrefs(context).getString(KEY_USB_CONFIG_CACHE, null);
if (jsonString == null || jsonString.isEmpty()) {
Log.d("SpUtils", "No UsbConfig cache found.");
return null; // 没有缓存
}
try {
// 使用 Gson 将 JSON 字符串转换回对象
com.google.gson.Gson gson = new com.google.gson.Gson();
UsbConfigXlsxBean config = gson.fromJson(jsonString, UsbConfigXlsxBean.class);
Log.d("SpUtils", "UsbConfig loaded from cache successfully.");
return config;
} catch (com.google.gson.JsonSyntaxException e) {
Log.e("SpUtils", "Failed to parse cached UsbConfig JSON.", e);
// 如果JSON损坏,清除错误的缓存
getPrefs(context).edit().remove(KEY_USB_CONFIG_CACHE).apply();
return null; // 解析失败
}
}
// 【新增】定义一个不可能出现在文件名中的特殊分隔符
private static final String PATH_SEPARATOR = "##_##";
/**
* 【已重构】将一个字符串数组(图片路径)保存到 SharedPreferences。
* 这个版本会先对数组进行排序,然后拼接成一个字符串来保证顺序。
* @param context Context
* @param key 用于保存的 Key
* @param paths 要保存的图片路径数组
*/
public static void saveImagePathSet(Context context, String key, String[] paths) {
SharedPreferences.Editor editor = getPrefs(context).edit();
if (paths == null || paths.length == 0) {
editor.remove(key).apply();
return;
}
// --- 核心修改 ---
// 1. 将数组转换为可排序的列表
List<String> pathList = new ArrayList<>(Arrays.asList(paths));
// 2. 定义一个能提取数字并进行排序的比较器
Comparator<String> numericalComparator = new Comparator<String>() {
private final Pattern pattern = Pattern.compile("(\\d+)"); // 正则表达式,用于找到文件名中的数字
@Override
public int compare(String s1, String s2) {
return Integer.compare(extractNumber(s1), extractNumber(s2));
}
private int extractNumber(String s) {
// 从完整路径中提取文件名
String fileName = new File(s).getName();
Matcher matcher = pattern.matcher(fileName);
if (matcher.find()) {
try {
// 返回找到的第一个数字序列
return Integer.parseInt(matcher.group(1));
} catch (NumberFormatException e) {
// 忽略解析失败的情况
}
}
// 如果文件名中没有数字,则返回0,或者可以根据需要进行其他处理
return 0;
}
};
// 3. 对列表进行排序
Collections.sort(pathList, numericalComparator);
// 4. 使用特殊分隔符将排序后的列表拼接成一个字符串
// 在现代Java (API 26+) 中,可以使用 String.join
// 为了更好的兼容性,我们使用 StringBuilder
StringBuilder sb = new StringBuilder();
for (int i = 0; i < pathList.size(); i++) {
sb.append(pathList.get(i));
if (i < pathList.size() - 1) {
sb.append(PATH_SEPARATOR);
}
}
// 5. 将拼接好的字符串存入 SharedPreferences
editor.putString(key, sb.toString()).apply();
}
/**
* 【已重构】从 SharedPreferences 中读取图片路径数组。
* 这个版本会读取拼接的字符串,并按分隔符拆分回数组,从而恢复顺序。
* @param context Context
* @param key 用于读取的 Key
* @return 返回一个已排序的字符串数组。如果未找到,则返回空数组。
*/
public static String[] getImagePathArray(Context context, String key) {
String concatenatedPaths = getPrefs(context).getString(key, null);
if (concatenatedPaths == null || concatenatedPaths.isEmpty()) {
return new String[0];
}
// 使用相同的分隔符将字符串拆分回数组
return concatenatedPaths.split(PATH_SEPARATOR);
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment