Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
T
test_webhook
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
1
Merge Requests
1
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
时海鑫
test_webhook
Commits
372d8daf
Commit
372d8daf
authored
Oct 10, 2025
by
时海鑫
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
test4
parent
54f17ca2
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
426 additions
and
0 deletions
+426
-0
ProtocolHelper.java
ProtocolHelper.java
+167
-0
SpUtils.java
SpUtils.java
+259
-0
No files found.
ProtocolHelper.java
0 → 100644
View file @
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
SpUtils.java
0 → 100644
View file @
372d8daf
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
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment