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
e424bd99
Commit
e424bd99
authored
Oct 10, 2025
by
时海鑫
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'dev' into 'master'
test4 See merge request
!18
parents
aef499f1
372d8daf
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 @
e424bd99
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 @
e424bd99
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