保驾护航:后台运维视角下的高速公路“生命线”

本文最后更新于:18 天前

路段版本

Sa-Token

2026 年 1 月 13 日

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
package com.tomm.framework.config;

import cn.dev33.satoken.interceptor.SaAnnotationInterceptor;
import cn.dev33.satoken.interceptor.SaRouteInterceptor;
import cn.dev33.satoken.jwt.StpLogicJwtForSimple;
import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.stp.StpLogic;
import cn.dev33.satoken.stp.StpUtil;
import com.tomm.common.utils.spring.SpringUtils;
import com.tomm.framework.config.properties.ExcludeUrlProperties;
import com.tomm.framework.config.properties.SecurityProperties;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
* sa-token 配置
*
* @author Lion Li
*/
@RequiredArgsConstructor
@Slf4j
@Configuration
public class SaTokenConfig implements WebMvcConfigurer {

private final SecurityProperties securityProperties;

/**
* 注册sa-token的拦截器
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册路由拦截器,自定义验证规则
registry.addInterceptor(new SaRouteInterceptor((request, response, handler) -> {
ExcludeUrlProperties excludeUrlProperties = SpringUtils.getBean(ExcludeUrlProperties.class);
// 登录验证 -- 排除多个路径
SaRouter
// 获取所有的
.match("/**")
// 排除下不需要拦截的
.notMatch(securityProperties.getExcludes())
.notMatch(excludeUrlProperties.getExcludes())
// 对未排除的路径进行检查
.check(() -> {
// 检查是否登录 是否有token
StpUtil.checkLogin();

// 有效率影响 用于临时测试
// if (log.isDebugEnabled()) {
// log.debug("剩余有效时间: {}", StpUtil.getTokenTimeout());
// log.debug("临时有效时间: {}", StpUtil.getTokenActivityTimeout());
// }

});
})).addPathPatterns("/**");
registry.addInterceptor(new SaAnnotationInterceptor()).addPathPatterns("/**");
}

@Bean
public StpLogic getStpLogicJwt() {
// Sa-Token 整合 jwt (简单模式)
return new StpLogicJwtForSimple();
}

}

核心方法解析。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package com.tomm.framework.config.properties;

import cn.hutool.core.util.ReUtil;
import com.tomm.common.annotation.Anonymous;
import com.tomm.common.utils.spring.SpringUtils;
import lombok.Getter;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.util.pattern.PathPattern;

import java.util.*;
import java.util.regex.Pattern;

/**
* 设置注解允许匿名访问的url
*
* @author Lion Li
*/
@Lazy
@Component
public class ExcludeUrlProperties implements InitializingBean {

private static final Pattern PATTERN = Pattern.compile("\\{(.*?)\\}");

@Getter
private final List<String> excludes = new ArrayList<>();

@Override
public void afterPropertiesSet() {
String asterisk = "*";
RequestMappingHandlerMapping mapping = SpringUtils.getBean("requestMappingHandlerMapping", RequestMappingHandlerMapping.class);
Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods();

map.keySet().forEach(info -> {
HandlerMethod handlerMethod = map.get(info);

// 获取方法上边的注解 替代path variable 为 *
Anonymous method = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), Anonymous.class);
Optional.ofNullable(method).ifPresent(anonymous -> {
Set<PathPattern> patterns = info.getPathPatternsCondition().getPatterns();
patterns.forEach(url -> {
excludes.add(ReUtil.replaceAll(url.getPatternString(), PATTERN, asterisk));
});
});

// 获取类上边的注解, 替代path variable 为 *
Anonymous controller = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), Anonymous.class);
Optional.ofNullable(controller).ifPresent(anonymous -> {
Set<PathPattern> patterns = info.getPathPatternsCondition().getPatterns();
patterns.forEach(url -> {
excludes.add(ReUtil.replaceAll(url.getPatternString(), PATTERN, asterisk));
});
});
});
}
}

afterPropertiesSet()方法

步骤1:获取所有接口映射

1
2
RequestMappingHandlerMapping mapping = SpringUtils.getBean("requestMappingHandlerMapping", RequestMappingHandlerMapping.class);
Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods();
  • 从 Spring 容器中获取 RequestMappingHandlerMapping(Spring MVC 的核心组件,存储所有Controller的映射信息)
  • 获取所有接口映射的 Map,key 是接口信息,value 是处理方法

步骤2:扫描方法上的 @Anonymous注解

1
2
3
4
5
6
7
8
9
10
// 获取方法上的注解
Anonymous method = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), Anonymous.class);
Optional.ofNullable(method).ifPresent(anonymous -> {
// 获取接口的所有URL模式
Set<PathPattern> patterns = info.getPathPatternsCondition().getPatterns();
patterns.forEach(url -> {
// 将路径变量替换为 *
excludes.add(ReUtil.replaceAll(url.getPatternString(), PATTERN, asterisk));
});
});
  • 如果方法上有 @Anonymous注解,收集其所有URL
  • 将路径变量(如 {id})替换为 *
  • 示例:/api/users/{id}/api/users/*

步骤3:扫描类上的 @Anonymous注解

1
2
3
4
5
// 获取类上的注解
Anonymous controller = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), Anonymous.class);
Optional.ofNullable(controller).ifPresent(anonymous -> {
// 类似处理
});
  • 如果 Controller 类上有 @Anonymous注解,该类下所有接口都允许匿名访问

小屏概览

2026 年 1 月 13 日

本机调试。

已部署的路段版本接口文档,测试环境:OpenAPI definition

路段版本接口文档,本机启动,OpenAPI definition

测试这个接口,POST 请求。

1
http://localhost:10057/smallScreenDispla/monitorSummary
1
2
3
{
"deptId": 181
}

小屏概览接口,查询本机的cpu、内存、磁盘、上下行参数

1
2
3
4
5
6
@Operation(summary = "小屏概览接口,查询本机的cpu、内存、磁盘、上下行参数")
@PostMapping("/monitorSummary")
public R<MonitorSummaryVo> monitorSummary(@RequestBody SyncAssetInfoDto syncAssetInfoDto) {
System.out.println("小屏概览接口,查询本机的cpu、内存、磁盘、上下行参数");
return R.ok(smallScreenDisplayService.monitorSummary(syncAssetInfoDto));
}
1
2
3
4
5
6
7
8
MonitorSummaryVo monitorSummaryVo = new MonitorSummaryVo();
//根据部门,查询监测主机的数据
List<SectionHostDeptMap> sectionHostDeptMapList = sectionHostDeptMapService.selectSectionHostDeptMapByHostTypeAndDeptId(
Lists.newArrayList(syncAssetInfoDto.getDeptId()), Lists.newArrayList((byte)10));
log.info("查询概览参数,获取本机资产数据结果:{}", JsonUtils.toJsonString(sectionHostDeptMapList));
if (CollUtil.isEmpty(sectionHostDeptMapList) || sectionHostDeptMapList.size() > RmgtMagicValue.ONE) {
throw new ServiceException("获取资产信息有误");
}
1
2
3
4
5
6
7
/**
* 查询资产主机列表
* @param deptIds 部门
* @param hostTypes 主机类型
* @return
*/
List<SectionHostDeptMap> selectSectionHostDeptMapByHostTypeAndDeptId(List<Long> deptIds, List<Byte> hostTypes);
1
2
3
4
@Override
public List<SectionHostDeptMap> selectSectionHostDeptMapByHostTypeAndDeptId(List<Long> deptIds, List<Byte> hostTypes){
return sectionHostDeptMapMapper.selectSectionHostDeptMapByHostTypeAndDeptId(deptIds, hostTypes);
}
1
2
3
4
5
6
7
8
/**
* 查询资产主机列表
* @param deptIds 部门
* @param hostTypes 主机类型
* @return
*/
List<SectionHostDeptMap> selectSectionHostDeptMapByHostTypeAndDeptId(@Param("deptIds") List<Long> deptIds, @Param("hostTypes") List<Byte> hostTypes);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<select id="selectSectionHostDeptMapByHostTypeAndDeptId" parameterType="java.lang.Byte" resultType="com.tomm.system.domain.SectionHostDeptMap">
SELECT
<include refid="Base_Column_List" />
FROM
t_section_host_dept_map
<where>
<if test="deptIds != null and deptIds.size() > 0">
AND dept_id IN
<foreach item="item" collection="deptIds" open="(" separator="," close=")">
#{item}
</foreach>
</if>
<if test="hostTypes != null and hostTypes.size() > 0">
AND host_type IN
<foreach item="item" collection="hostTypes" open="(" separator="," close=")">
#{item}
</foreach>
</if>
</where>
</select>

Lists.newArrayList((byte)10)创建了一个包含单个字节值 10 的 List,表示查询主机类型为 10 的记录。

小屏监测

2026 年 1 月 15 日

1
2
3
4
5
6
7
8
9
10
log.info("查询路段、门架、车道、设备等主机列表,入参:{}", JsonUtils.toJsonString(monitorAssetDto));
if (CollUtil.isEmpty(monitorAssetDto.getMonitorHostTypeAll())) {
throw new ServiceException("监测的主机类型列表为空");
}
if (ObjUtil.isEmpty(monitorAssetDto.getMonitorHostType())) {
throw new ServiceException("当前监测的主机类型为空");
}
if (ObjUtil.isEmpty(monitorAssetDto.getDeptId())) {
throw new ServiceException("当前监测的部门为空");
}
1

1
2
3
4
5
/**
* 根据部门id查询部门及所有下属部门信息
* @param deptId 部门id
*/
List<SysDept> selectSysDeptAndSubDeptById(@Param("deptId") Long deptId);
1
2
3
4
5
6
7
8
9
<select id="selectSysDeptAndSubDeptById" parameterType="java.lang.Long"
resultType="com.tomm.common.core.domain.entity.SysDept">
SELECT
<include refid="Base_Column_List"/>
FROM sys_dept
WHERE (del_flag = '0' and (dept_id=#{deptId} or FIND_IN_SET(#{deptId},ancestors)))
ORDER BY parent_id ASC,
order_num ASC
</select>
1
2
3
4
5
6
7
8
9
/**
* 根据部门、类型统计主机数量
*
* @param deptIds 部门ID(为空时查询所有)
* @param hostTypes 主机类型
* @return 主机数
*/
Long countHostsByDeptAndType(@Param("deptIds") List<Long> deptIds, @Param("hostTypes") List<Byte> hostTypes);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
MonitorAssetVo monitorAssetVo = new MonitorAssetVo();
for (String type : monitorAssetDto.getMonitorHostTypeAll()) {
if (MonitorHostTypeEnum.STATION.getKey().equals(type)) {
//统计站服务器数
monitorAssetVo.setStationHostNum(sectionHostDeptMapService.countHostsByDeptAndType(deptIds, MonitorHostTypeEnum.STATION.getValue()));
}
if (MonitorHostTypeEnum.LANE.getKey().equals(type)) {
//统计车道工控机数
monitorAssetVo.setLaneHostNum(sectionHostDeptMapService.countHostsByDeptAndType(deptIds, MonitorHostTypeEnum.LANE.getValue()));
}
if (MonitorHostTypeEnum.GANTRY.getKey().equals(type)) {
//统计门架工控机数
monitorAssetVo.setGantryHostNum(sectionHostDeptMapService.countHostsByDeptAndType(deptIds, MonitorHostTypeEnum.GANTRY.getValue()));
}
}

站服务器数,车道工控机数,门架工控机。t_section_host_dept_map

1
2
3
4
5
6
7
8
9
10
//查询主机列表
Page<MonitorAssetHost> monitorAssetHostList = sectionHostDeptMapService.selectAssetHostListPage(deptIds,
hostTypes, monitorAssetDto.getMonitorStateType(), monitorAssetDto.build());
//转换HostTypeName
for (MonitorAssetHost monitorAssetHost : monitorAssetHostList.getRecords()) {
monitorAssetHost.setHostTypeName(HostTypeEnum.getValue(monitorAssetHost.getHostType()));
}
monitorAssetVo.setHostTos(monitorAssetHostList);
log.info("查询路段、门架、车道、设备等主机列表,返参:{}", JsonUtils.toJsonString(monitorAssetVo));
return monitorAssetVo;
1
2
3
4
5
6
7
8
9
/**
* 查询资产主机列表,带监测状态
*
* @param deptIds 部门ID集合(为空时查询所有)
* @param hostTypes 主机类型
* @param page 分页参数
* @return 资产主机状态列表
*/
Page<MonitorAssetHost> selectAssetHostListPage(List<Long> deptIds, List<Byte> hostTypes, String monitorStateType, Page<MonitorAssetHost> page);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<select id="selectAssetHostListPage" parameterType="map" resultType="com.tomm.system.domain.vo.MonitorAssetHost">
SELECT
shdm.host_ip,
shdm.host_type,
shdm.update_time,
h.name as hostName,
if(h.`status`='0','1','0') monitorState
FROM
t_section_host_dept_map shdm
left JOIN
zabbix.hosts h ON h.hostid = shdm.host_id
<where>
<if test="deptIds != null and deptIds.size() > 0">
AND shdm.dept_id IN
<foreach item="item" collection="deptIds" open="(" separator="," close=")">
#{item}
</foreach>
</if>
<if test="hostTypes != null and hostTypes.size() > 0">
AND shdm.host_type IN
<foreach item="item" collection="hostTypes" open="(" separator="," close=")">
#{item}
</foreach>
</if>
<if test="monitorStateType != null and monitorStateType != ''">
AND h.`status`!= #{monitorStateType}
</if>
</where>
</select>

这个 page 怎么没用过。

1
2
3
4
5
6
7
8
9
10
11
/**
* 查询资产主机列表,带监测状态
*
* @param deptIds 部门ID集合(为空时查询所有)
* @param hostTypes 主机类型
* @param page 分页参数
* @return 资产主机状态列表
*/
Page<MonitorAssetHost> selectAssetHostListPage(@Param("deptIds") List<Long> deptIds, @Param("hostTypes") List<Byte> hostTypes,
@Param("monitorStateType") String monitorStateType, Page<MonitorAssetHost> page);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<select id="selectAssetHostListPage" parameterType="map" resultType="com.tomm.system.domain.vo.MonitorAssetHost">
SELECT
shdm.host_ip,
shdm.host_type,
shdm.update_time,
h.name as hostName,
if(h.`status`='0','1','0') monitorState
FROM
t_section_host_dept_map shdm
left JOIN
zabbix.hosts h ON h.hostid = shdm.host_id
<where>
<if test="deptIds != null and deptIds.size() > 0">
AND shdm.dept_id IN
<foreach item="item" collection="deptIds" open="(" separator="," close=")">
#{item}
</foreach>
</if>
<if test="hostTypes != null and hostTypes.size() > 0">
AND shdm.host_type IN
<foreach item="item" collection="hostTypes" open="(" separator="," close=")">
#{item}
</foreach>
</if>
<if test="monitorStateType != null and monitorStateType != ''">
AND h.`status`!= #{monitorStateType}
</if>
</where>
</select>

有问题。所以这里构造的page对象,PageSize竟然是0,pageNum是1,这不对吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public <T> Page<T> build() {
Integer pageNum = ObjectUtil.defaultIfNull(getPageNum(), DEFAULT_PAGE_NUM);
Integer pageSize = ObjectUtil.defaultIfNull(getPageSize(), DEFAULT_PAGE_SIZE);
if (pageNum <= 0) {
pageNum = DEFAULT_PAGE_NUM;
}
// 处理 pageSize
if (pageSize == null || pageSize <= 0) {
pageSize = DEFAULT_PAGE_SIZE;
}
Page<T> page = new Page<>(pageNum, pageSize);
OrderItem orderItem = buildOrderItem();
if (ObjectUtil.isNotNull(orderItem)) {
page.addOrder(orderItem);
}
return page;
}

成功了,PageSize 默认查询所有记录。

罗芬科技

主板

2026 年 2 月 9 日

(99+ 封私信 / 81 条消息) 电脑主板主要由那些部分组成,它们的作用是什么? - 知乎

固态硬盘接口,规格。

SATA、mSATA、M.2、M.2(NVMe)、PCIE固态硬盘接口详解-腾讯云开发者社区-腾讯云

1
SATA一般指串行ATA。 串行ATA的全称是Serial Advanced Technology Attachment,是由Intel、IBM、Dell、APT、Maxtor和Seagate公司共同提出的硬盘接口规范,在IDF Fall 2001大会上,Seagate宣布了Serial ATA 10标准,正式宣告了SATA规范的确立。

SATA硬盘接口_百度百科

m.2接口sata和pcie区别_M2固态硬盘安装方法-腾讯云开发者社区-腾讯云

PCIE 接口。

1
PCI-E接口:在传统SATA硬盘中,当我们进行数据操作时,数据会先从硬盘读取到内存,再将数据提取至CPU内部进行计算,计算后写入内存,存储至硬盘中;而PCI-E就不一样了,数据直接通过总线与CPU直连,省去了内存调用硬盘的过程,传输效率与速度都成倍提升。

今天才知道固态硬盘原来还有这么多种-腾讯云开发者社区-腾讯云

(99+ 封私信 / 81 条消息) pcie 接口长什么样呢?它有哪些用处呢? - 知乎

工控主板上PCIE插槽有什么作用?

M.2接口。

内存条插槽规格。

标书

2026 年 2 月 3 日

📊 招标投标全流程解析,详细步骤一览 📊

大彩

2026 年 2 月 2 日

VisualTFT_开发软件_广州大彩专注组态串口屏,国内首创《虚拟串口屏》,大彩屏无需购买硬件!主营串口屏,串口显示屏,86面板,串口彩屏,触摸屏,HMI人机界面

QT环境

2026 年 1 月 30 日

麒麟系统离线极速安装QT_麒麟系统安装qt-CSDN博客

国产Linux麒麟系统QT安装及其之后安装QChart库步骤_linux 下 qt安装程序库-CSDN博客

1
2
3
4
5
6
7
8
9
10
11
12
loongson@loongson-pc:~$ ./qt-opensource-linux-x64-5.14.2.run
-bash: ./qt-opensource-linux-x64-5.14.2.run: 权限不够
loongson@loongson-pc:~$ chmod 777 ./qt-opensource-linux-x64-5.14.2.run
loongson@loongson-pc:~$ ./qt-opensource-linux-x64-5.14.2.run
not found LASX, use 128-bit vectors
qt.qpa.xcb: could not connect to display
qt.qpa.plugin: Could not load the Qt platform plugin "xcb" in "" even though it was found.
This application failed to start because no Qt platform plugin could be initialized. Reinstalling the application may fix this problem.

Available platform plugins are: minimal, xcb.

已放弃 (核心已转储)

Qt5.15下载和安装教程「建议收藏」-腾讯云开发者社区-腾讯云

Git认证

2026 年 1 月 28 日

1
ssh -T git@101.227.48.102 -p 2
1
ssh -Tvvv git@101.227.48.102 -p 2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
C:\WINDOWS\system32>ssh -Tvvv git@101.227.48.102 -p 22
OpenSSH_for_Windows_9.5p1, LibreSSL 3.8.2
debug1: Reading configuration data C:\\Users\\Lenovo/.ssh/config
debug3: Failed to open file:C:/ProgramData/ssh/ssh_config error:2
debug2: resolve_canonicalize: hostname 101.227.48.102 is address
debug3: expanded UserKnownHostsFile '~/.ssh/known_hosts' -> 'C:\\Users\\Lenovo/.ssh/known_hosts'
debug3: expanded UserKnownHostsFile '~/.ssh/known_hosts2' -> 'C:\\Users\\Lenovo/.ssh/known_hosts2'
debug3: ssh_connect_direct: entering
debug1: Connecting to 101.227.48.102 [101.227.48.102] port 22.
debug1: Connection established.
debug1: identity file C:\\Users\\Lenovo/.ssh/id_rsa type 0
debug3: Failed to open file:C:/Users/Lenovo/.ssh/id_rsa-cert error:2
debug3: Failed to open file:C:/Users/Lenovo/.ssh/id_rsa-cert.pub error:2
debug3: failed to open file:C:/Users/Lenovo/.ssh/id_rsa-cert error:2
debug1: identity file C:\\Users\\Lenovo/.ssh/id_rsa-cert type -1
debug3: Failed to open file:C:/Users/Lenovo/.ssh/id_ecdsa error:2
debug3: Failed to open file:C:/Users/Lenovo/.ssh/id_ecdsa.pub error:2
debug3: failed to open file:C:/Users/Lenovo/.ssh/id_ecdsa error:2
debug1: identity file C:\\Users\\Lenovo/.ssh/id_ecdsa type -1
debug3: Failed to open file:C:/Users/Lenovo/.ssh/id_ecdsa-cert error:2
debug3: Failed to open file:C:/Users/Lenovo/.ssh/id_ecdsa-cert.pub error:2
debug3: failed to open file:C:/Users/Lenovo/.ssh/id_ecdsa-cert error:2
debug1: identity file C:\\Users\\Lenovo/.ssh/id_ecdsa-cert type -1
debug3: Failed to open file:C:/Users/Lenovo/.ssh/id_ecdsa_sk error:2
debug3: Failed to open file:C:/Users/Lenovo/.ssh/id_ecdsa_sk.pub error:2
debug3: failed to open file:C:/Users/Lenovo/.ssh/id_ecdsa_sk error:2
debug1: identity file C:\\Users\\Lenovo/.ssh/id_ecdsa_sk type -1
debug3: Failed to open file:C:/Users/Lenovo/.ssh/id_ecdsa_sk-cert error:2
debug3: Failed to open file:C:/Users/Lenovo/.ssh/id_ecdsa_sk-cert.pub error:2
debug3: failed to open file:C:/Users/Lenovo/.ssh/id_ecdsa_sk-cert error:2
debug1: identity file C:\\Users\\Lenovo/.ssh/id_ecdsa_sk-cert type -1
debug1: identity file C:\\Users\\Lenovo/.ssh/id_ed25519 type 3
debug3: Failed to open file:C:/Users/Lenovo/.ssh/id_ed25519-cert error:2
debug3: Failed to open file:C:/Users/Lenovo/.ssh/id_ed25519-cert.pub error:2
debug3: failed to open file:C:/Users/Lenovo/.ssh/id_ed25519-cert error:2
debug1: identity file C:\\Users\\Lenovo/.ssh/id_ed25519-cert type -1
debug3: Failed to open file:C:/Users/Lenovo/.ssh/id_ed25519_sk error:2
debug3: Failed to open file:C:/Users/Lenovo/.ssh/id_ed25519_sk.pub error:2
debug3: failed to open file:C:/Users/Lenovo/.ssh/id_ed25519_sk error:2
debug1: identity file C:\\Users\\Lenovo/.ssh/id_ed25519_sk type -1
debug3: Failed to open file:C:/Users/Lenovo/.ssh/id_ed25519_sk-cert error:2
debug3: Failed to open file:C:/Users/Lenovo/.ssh/id_ed25519_sk-cert.pub error:2
debug3: failed to open file:C:/Users/Lenovo/.ssh/id_ed25519_sk-cert error:2
debug1: identity file C:\\Users\\Lenovo/.ssh/id_ed25519_sk-cert type -1
debug3: Failed to open file:C:/Users/Lenovo/.ssh/id_xmss error:2
debug3: Failed to open file:C:/Users/Lenovo/.ssh/id_xmss.pub error:2
debug3: failed to open file:C:/Users/Lenovo/.ssh/id_xmss error:2
debug1: identity file C:\\Users\\Lenovo/.ssh/id_xmss type -1
debug3: Failed to open file:C:/Users/Lenovo/.ssh/id_xmss-cert error:2
debug3: Failed to open file:C:/Users/Lenovo/.ssh/id_xmss-cert.pub error:2
debug3: failed to open file:C:/Users/Lenovo/.ssh/id_xmss-cert error:2
debug1: identity file C:\\Users\\Lenovo/.ssh/id_xmss-cert type -1
debug3: Failed to open file:C:/Users/Lenovo/.ssh/id_dsa error:2
debug3: Failed to open file:C:/Users/Lenovo/.ssh/id_dsa.pub error:2
debug3: failed to open file:C:/Users/Lenovo/.ssh/id_dsa error:2
debug1: identity file C:\\Users\\Lenovo/.ssh/id_dsa type -1
debug3: Failed to open file:C:/Users/Lenovo/.ssh/id_dsa-cert error:2
debug3: Failed to open file:C:/Users/Lenovo/.ssh/id_dsa-cert.pub error:2
debug3: failed to open file:C:/Users/Lenovo/.ssh/id_dsa-cert error:2
debug1: identity file C:\\Users\\Lenovo/.ssh/id_dsa-cert type -1
debug1: Local version string SSH-2.0-OpenSSH_for_Windows_9.5
debug1: Remote protocol version 2.0, remote software version OpenSSH_8.0
debug1: compat_banner: match: OpenSSH_8.0 pat OpenSSH* compat 0x04000000
debug2: fd 3 setting O_NONBLOCK
debug1: Authenticating to 101.227.48.102:22 as 'git'
debug3: record_hostkey: found key type ED25519 in file C:\\Users\\Lenovo/.ssh/known_hosts:10
debug3: load_hostkeys_file: loaded 1 keys from 101.227.48.102
debug3: Failed to open file:C:/Users/Lenovo/.ssh/known_hosts2 error:2
debug1: load_hostkeys: fopen C:\\Users\\Lenovo/.ssh/known_hosts2: No such file or directory
debug3: Failed to open file:C:/ProgramData/ssh/ssh_known_hosts error:2
debug1: load_hostkeys: fopen __PROGRAMDATA__\\ssh/ssh_known_hosts: No such file or directory
debug3: Failed to open file:C:/ProgramData/ssh/ssh_known_hosts2 error:2
debug1: load_hostkeys: fopen __PROGRAMDATA__\\ssh/ssh_known_hosts2: No such file or directory
debug3: order_hostkeyalgs: have matching best-preference key type ssh-ed25519-cert-v01@openssh.com, using HostkeyAlgorithms verbatim
debug3: send packet: type 20
debug1: SSH2_MSG_KEXINIT sent
debug3: receive packet: type 20
debug1: SSH2_MSG_KEXINIT received
debug2: local client KEXINIT proposal
debug2: KEX algorithms: curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group14-sha256,ext-info-c,kex-strict-c-v00@openssh.com
debug2: host key algorithms: ssh-ed25519-cert-v01@openssh.com,ecdsa-sha2-nistp256-cert-v01@openssh.com,ecdsa-sha2-nistp384-cert-v01@openssh.com,ecdsa-sha2-nistp521-cert-v01@openssh.com,sk-ssh-ed25519-cert-v01@openssh.com,sk-ecdsa-sha2-nistp256-cert-v01@openssh.com,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,ssh-ed25519,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,sk-ssh-ed25519@openssh.com,sk-ecdsa-sha2-nistp256@openssh.com,rsa-sha2-512,rsa-sha2-256
debug2: ciphers ctos: chacha20-poly1305@openssh.com,aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com
debug2: ciphers stoc: chacha20-poly1305@openssh.com,aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com
debug2: MACs ctos: umac-64-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,umac-64@openssh.com,umac-128@openssh.com,hmac-sha2-256,hmac-sha2-512
debug2: MACs stoc: umac-64-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,umac-64@openssh.com,umac-128@openssh.com,hmac-sha2-256,hmac-sha2-512
debug2: compression ctos: none,zlib@openssh.com,zlib
debug2: compression stoc: none,zlib@openssh.com,zlib
debug2: languages ctos:
debug2: languages stoc:
debug2: first_kex_follows 0
debug2: reserved 0
debug2: peer server KEXINIT proposal
debug2: KEX algorithms: curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group14-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group-exchange-sha1,diffie-hellman-group14-sha1
debug2: host key algorithms: rsa-sha2-512,rsa-sha2-256,ssh-rsa,ecdsa-sha2-nistp256,ssh-ed25519
debug2: ciphers ctos: aes256-gcm@openssh.com,chacha20-poly1305@openssh.com,aes256-ctr,aes256-cbc,aes128-gcm@openssh.com,aes128-ctr,aes128-cbc
debug2: ciphers stoc: aes256-gcm@openssh.com,chacha20-poly1305@openssh.com,aes256-ctr,aes256-cbc,aes128-gcm@openssh.com,aes128-ctr,aes128-cbc
debug2: MACs ctos: hmac-sha2-256-etm@openssh.com,hmac-sha1-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha2-256,hmac-sha1,umac-128@openssh.com,hmac-sha2-512
debug2: MACs stoc: hmac-sha2-256-etm@openssh.com,hmac-sha1-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha2-256,hmac-sha1,umac-128@openssh.com,hmac-sha2-512
debug2: compression ctos: none,zlib@openssh.com
debug2: compression stoc: none,zlib@openssh.com
debug2: languages ctos:
debug2: languages stoc:
debug2: first_kex_follows 0
debug2: reserved 0
debug1: kex: algorithm: curve25519-sha256
debug1: kex: host key algorithm: ssh-ed25519
debug1: kex: server->client cipher: chacha20-poly1305@openssh.com MAC: <implicit> compression: none
debug1: kex: client->server cipher: chacha20-poly1305@openssh.com MAC: <implicit> compression: none
debug3: send packet: type 30
debug1: expecting SSH2_MSG_KEX_ECDH_REPLY
debug3: receive packet: type 31
debug1: SSH2_MSG_KEX_ECDH_REPLY received
debug1: Server host key: ssh-ed25519 SHA256:CPXsWA3qquwUoOVeMDDaHHbgsc1zmJWrM01Yoy6hMmI
debug3: record_hostkey: found key type ED25519 in file C:\\Users\\Lenovo/.ssh/known_hosts:10
debug3: load_hostkeys_file: loaded 1 keys from 101.227.48.102
debug3: Failed to open file:C:/Users/Lenovo/.ssh/known_hosts2 error:2
debug1: load_hostkeys: fopen C:\\Users\\Lenovo/.ssh/known_hosts2: No such file or directory
debug3: Failed to open file:C:/ProgramData/ssh/ssh_known_hosts error:2
debug1: load_hostkeys: fopen __PROGRAMDATA__\\ssh/ssh_known_hosts: No such file or directory
debug3: Failed to open file:C:/ProgramData/ssh/ssh_known_hosts2 error:2
debug1: load_hostkeys: fopen __PROGRAMDATA__\\ssh/ssh_known_hosts2: No such file or directory
debug1: Host '101.227.48.102' is known and matches the ED25519 host key.
debug1: Found key in C:\\Users\\Lenovo/.ssh/known_hosts:10
debug3: send packet: type 21
debug2: ssh_set_newkeys: mode 1
debug1: rekey out after 134217728 blocks
debug1: SSH2_MSG_NEWKEYS sent
debug1: expecting SSH2_MSG_NEWKEYS
debug3: receive packet: type 21
debug1: SSH2_MSG_NEWKEYS received
debug2: ssh_set_newkeys: mode 0
debug1: rekey in after 134217728 blocks
debug3: ssh_get_authentication_socket_path: path '\\\\.\\pipe\\openssh-ssh-agent'
debug3: unable to connect to pipe \\\\.\\pipe\\openssh-ssh-agent, error: 2
debug1: get_agent_identities: ssh_get_authentication_socket: No such file or directory
debug1: Will attempt key: C:\\Users\\Lenovo/.ssh/id_rsa RSA SHA256:18HfOCQvT8zCV5AVrzxYRzhwO4W0HB0pOhzCk8klaLw
debug1: Will attempt key: C:\\Users\\Lenovo/.ssh/id_ecdsa
debug1: Will attempt key: C:\\Users\\Lenovo/.ssh/id_ecdsa_sk
debug1: Will attempt key: C:\\Users\\Lenovo/.ssh/id_ed25519 ED25519 SHA256:314LS48LcdNWWV6CRzNlB0/oeBGMioEXuiYGuMtMAAM
debug1: Will attempt key: C:\\Users\\Lenovo/.ssh/id_ed25519_sk
debug1: Will attempt key: C:\\Users\\Lenovo/.ssh/id_xmss
debug1: Will attempt key: C:\\Users\\Lenovo/.ssh/id_dsa
debug2: pubkey_prepare: done
debug3: send packet: type 5
debug3: receive packet: type 7
debug1: SSH2_MSG_EXT_INFO received
debug1: kex_input_ext_info: server-sig-algs=<ssh-ed25519,ssh-rsa,rsa-sha2-256,rsa-sha2-512,ssh-dss,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521>
debug3: receive packet: type 6
debug2: service_accept: ssh-userauth
debug1: SSH2_MSG_SERVICE_ACCEPT received
debug3: send packet: type 50
debug3: receive packet: type 51
debug1: Authentications that can continue: publickey,gssapi-keyex,gssapi-with-mic,password
debug3: start over, passed a different list publickey,gssapi-keyex,gssapi-with-mic,password
debug3: preferred publickey,keyboard-interactive,password
debug3: authmethod_lookup publickey
debug3: remaining preferred: keyboard-interactive,password
debug3: authmethod_is_enabled publickey
debug1: Next authentication method: publickey
debug1: Offering public key: C:\\Users\\Lenovo/.ssh/id_rsa RSA SHA256:18HfOCQvT8zCV5AVrzxYRzhwO4W0HB0pOhzCk8klaLw
debug3: send packet: type 50
debug2: we sent a publickey packet, wait for reply
debug3: receive packet: type 51
debug1: Authentications that can continue: publickey,gssapi-keyex,gssapi-with-mic,password
debug1: Trying private key: C:\\Users\\Lenovo/.ssh/id_ecdsa
debug3: no such identity: C:\\Users\\Lenovo/.ssh/id_ecdsa: No such file or directory
debug1: Trying private key: C:\\Users\\Lenovo/.ssh/id_ecdsa_sk
debug3: no such identity: C:\\Users\\Lenovo/.ssh/id_ecdsa_sk: No such file or directory
debug1: Offering public key: C:\\Users\\Lenovo/.ssh/id_ed25519 ED25519 SHA256:314LS48LcdNWWV6CRzNlB0/oeBGMioEXuiYGuMtMAAM
debug3: send packet: type 50
debug2: we sent a publickey packet, wait for reply
debug3: receive packet: type 51
debug1: Authentications that can continue: publickey,gssapi-keyex,gssapi-with-mic,password
debug1: Trying private key: C:\\Users\\Lenovo/.ssh/id_ed25519_sk
debug3: no such identity: C:\\Users\\Lenovo/.ssh/id_ed25519_sk: No such file or directory
debug1: Trying private key: C:\\Users\\Lenovo/.ssh/id_xmss
debug3: no such identity: C:\\Users\\Lenovo/.ssh/id_xmss: No such file or directory
debug1: Trying private key: C:\\Users\\Lenovo/.ssh/id_dsa
debug3: no such identity: C:\\Users\\Lenovo/.ssh/id_dsa: No such file or directory
debug2: we did not send a packet, disable method
debug3: authmethod_lookup password
debug3: remaining preferred: ,password
debug3: authmethod_is_enabled password
debug1: Next authentication method: password
git@101.227.48.102's password:
1
2
3
4
5
6
7
C:\WINDOWS\system32>ssh -T git@101.227.48.102 -i "C:\Users\Lenovo\.ssh\id_rsa"
The authenticity of host '101.227.48.102 (101.227.48.102)' can't be established.
ED25519 key fingerprint is SHA256:CPXsWA3qquwUoOVeMDDaHHbgsc1zmJWrM01Yoy6hMmI.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '101.227.48.102' (ED25519) to the list of known hosts.
git@101.227.48.102's password:

known hosts 文件内容。

known_hosts 是 SSH 的 “信任名单”,存储了你确认过的、可信的远程服务器的公钥信息。

1
101.227.48.102 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICtguMSSuf4Fk0NbTJiq0oto20+x9NqDxLMDV19ahMOX
1
2
3
4
5
# 获取指定 IP 的所有算法公钥
ssh-keyscan 101.227.48.102

# 只获取 ED25519 算法的公钥(精准匹配你本地的记录)
ssh-keyscan -t ed25519 101.227.48.102
1
2
3
4
5
6
7
8
9
C:\WINDOWS\system32>ssh-keyscan 101.227.48.102
# 101.227.48.102:22 SSH-2.0-OpenSSH_8.0
101.227.48.102 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC8T2ttfbUFdbRAImGUYy1yRfV4o3u3uFZF2fDntvq9VvHOT1h4HINIAlXqvq+Pk0DwdOnXBn5AwTYfY0H0Tar8WjddvG1Hk5W4QCpqMWsyQ9FqwYtYgpQQEXOm9sxlLu13mAjI94+Tz1ozjmMjcc+JTPJ57In8nkLzLcfiOk9RApJ26jM2Vww7c3uAzhQCqRdjzBeozADgJT2kJfltcs+9s9OjSNSl3ff8SWvRlbsnGX2WLDFtv5/NAVGXVfn/iiZPNKu6lUy/xwGycJYjq2zRplcmBwYXDt9x5ZUC/n/4WjitT5cvpMf13LZzncV7PJ4PPXn0s2Uo5hcW4S/hWxxovOe2gW/iOTosaUmJGJaXJfA74PTBvAH4MwkFzV19DKjJQ5OMzixHhTE+B7z9JELKarDen2bMUR5kFrnZK+guK4B5zfeZ/YEkH9/PM6I719hKbmWp6ubc9bpKHs9dUQfzHHeuBmt2L3YxA/jVmjM6BJB9aL9wzIzALs7wgMp+hN8=
# 101.227.48.102:22 SSH-2.0-OpenSSH_8.0
101.227.48.102 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBMk9rDm6ntFgVbjwbKKZHsOgZ7XoxVLSxu1/eGMXgJx30jPlN6DxVh7dCy13LRfxU/fKrnqCHoz3EvBP0Ii6/H0=
# 101.227.48.102:22 SSH-2.0-OpenSSH_8.0
101.227.48.102 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICtguMSSuf4Fk0NbTJiq0oto20+x9NqDxLMDV19ahMOX
# 101.227.48.102:22 SSH-2.0-OpenSSH_8.0
# 101.227.48.102:22 SSH-2.0-OpenSSH_8.0
1
2
3
4
5
6
7
8
# 1. 先删除之前错误的配置
git config --global --unset core.sshCommand

# 2. 配置正确的路径(用正斜杠,Git Bash 识别)
git config --global core.sshCommand "ssh -i C:/Users/Lenovo/.ssh/id_rsa"

# 3. 验证配置是否正确
git config --global core.sshCommand
1
git clone http://101.227.48.102:10080/zhm/tomm-rmgt-server.git
1
2
3
4
5
6
7
8
9
10
11
12
13
***************************
APPLICATION FAILED TO START
***************************

Description:

Failed to bind properties under 'spring.redis.password' to java.lang.String:

Reason: java.lang.IllegalStateException: either 'jasypt.encryptor.password' or one of ['jasypt.encryptor.private-key-string', 'jasypt.encryptor.private-key-location'] must be provided for Password-based or Asymmetric encryption

Action:

Update your application's configuration
1
2
3
4
5
6
7
8
9
10
# Spring配置
spring:
application:
name: ${ruoyi.name}
# 资源信息
messages:
# 国际化资源文件路径
basename: i18n/messages
profiles:
active: dev
1
rootaxdQ@1121.1226

访问令牌。

1
glpat-zx59RyfXdquQ2xBRmchz

服务单元

2026 年 1 月 27 日

Linux怎么使用systemctl管理服务-linux运维-PHP中文网

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[Unit]
Description=触摸屏
After=network.target

[Service]
Type=simple
User=root
WorkingDirectory=/home/memory/touchScreen/src/dist
ExecStart=/home/memory/touchScreen/src/dist/__init__
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal
SyslogIdentifier=my-app

[Install]
WantedBy=multi-user.target
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 重载systemd配置
sudo systemctl daemon-reload

# 启动服务
sudo systemctl start touchscreen

# 查看状态
sudo systemctl status touchscreen

# 查看日志
sudo journalctl -u touchscreen -f

# 设置开机自启
sudo systemctl enable touchscreen

# 查看是否启用
sudo systemctl is-enabled touchscreen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74

-- The unit touchscreen.service has entered the 'failed' state with result 'exit-code'.
Jan 27 02:10:43 localhost.localdomain setroubleshoot[3206]: AnalyzeThread.run(): Cancel pending alarm
Jan 27 02:10:43 localhost.localdomain dbus-daemon[982]: [system] Activating service name='org.fedoraproject.SetroubleshootPrivileged' requested by ':1.83' (uid=977 pid=3206 comm="/usr/libexec/platform-python -Es /usr/sbin/setroub" label="system_u:system_r:setroubleshootd_t:s0-s0:c0.c1023") (using servicehelper)
Jan 27 02:10:43 localhost.localdomain dbus-daemon[982]: [system] Successfully activated service 'org.fedoraproject.SetroubleshootPrivileged'
Jan 27 02:10:44 localhost.localdomain setroubleshoot[3206]: SELinux is preventing /usr/lib/systemd/systemd from execute access on the file touchscreen. For complete SELinux messages run: sealert -l a1107bcb-4757-4dc0-80a0-c58e2657ca97
Jan 27 02:10:44 localhost.localdomain setroubleshoot[3206]: SELinux is preventing /usr/lib/systemd/systemd from execute access on the file touchscreen.

***** Plugin catchall (100. confidence) suggests **************************

If you believe that systemd should be allowed execute access on the touchscreen file by default.
Then you should report this as a bug.
You can generate a local policy module to allow this access.
Do
allow this access for now by executing:
# ausearch -c '(__init__)' --raw | audit2allow -M my-init
# semodule -X 300 -i my-init.pp

Jan 27 02:10:44 localhost.localdomain setroubleshoot[3206]: AnalyzeThread.run(): Set alarm timeout to 10
Jan 27 02:10:53 localhost.localdomain systemd[1]: touchscreen.service: Service RestartSec=10s expired, scheduling restart.
Jan 27 02:10:53 localhost.localdomain systemd[1]: touchscreen.service: Scheduled restart job, restart counter is at 80.
-- Subject: Automatic restarting of a unit has been scheduled
-- Defined-By: systemd
-- Support: https://access.redhat.com/support
--
-- Automatic restarting of the unit touchscreen.service has been scheduled, as the result for
-- the configured Restart= setting for the unit.
Jan 27 02:10:53 localhost.localdomain systemd[1]: Stopped 触摸屏.
-- Subject: Unit touchscreen.service has finished shutting down
-- Defined-By: systemd
-- Support: https://access.redhat.com/support
--
-- Unit touchscreen.service has finished shutting down.
Jan 27 02:10:53 localhost.localdomain systemd[1]: Started 触摸屏.
-- Subject: Unit touchscreen.service has finished start-up
-- Defined-By: systemd
-- Support: https://access.redhat.com/support
--
-- Unit touchscreen.service has finished starting up.
--
-- The start-up result is done.
Jan 27 02:10:53 localhost.localdomain systemd[3782]: touchscreen.service: Failed to execute command: Permission denied
Jan 27 02:10:53 localhost.localdomain systemd[3782]: touchscreen.service: Failed at step EXEC spawning /home/memory/touchScreen/src/dist/touchscreen: Permission denied
-- Subject: Process /home/memory/touchScreen/src/dist/touchscreen could not be executed
-- Defined-By: systemd
-- Support: https://access.redhat.com/support
--
-- The process /home/memory/touchScreen/src/dist/touchscreen could not be executed and failed.
--
-- The error number returned by this process is 13.
Jan 27 02:10:53 localhost.localdomain systemd[1]: touchscreen.service: Main process exited, code=exited, status=203/EXEC
Jan 27 02:10:53 localhost.localdomain systemd[1]: touchscreen.service: Failed with result 'exit-code'.
-- Subject: Unit failed
-- Defined-By: systemd
-- Support: https://access.redhat.com/support
--
-- The unit touchscreen.service has entered the 'failed' state with result 'exit-code'.
Jan 27 02:10:53 localhost.localdomain setroubleshoot[3206]: AnalyzeThread.run(): Cancel pending alarm
Jan 27 02:10:53 localhost.localdomain setroubleshoot[3206]: SELinux is preventing /usr/lib/systemd/systemd from execute access on the file touchscreen. For complete SELinux messages run: sealert -l a1107bcb-4757-4dc0-80a0-c58e2657ca97
Jan 27 02:10:53 localhost.localdomain setroubleshoot[3206]: SELinux is preventing /usr/lib/systemd/systemd from execute access on the file touchscreen.

***** Plugin catchall (100. confidence) suggests **************************

If you believe that systemd should be allowed execute access on the touchscreen file by default.
Then you should report this as a bug.
You can generate a local policy module to allow this access.
Do
allow this access for now by executing:
# ausearch -c '(__init__)' --raw | audit2allow -M my-init
# semodule -X 300 -i my-init.pp

Jan 27 02:10:53 localhost.localdomain setroubleshoot[3206]: AnalyzeThread.run(): Set alarm timeout to 10
Jan 27 02:10:53 localhost.localdomain sudo[3789]: root : TTY=pts/0 ; PWD=/etc/systemd/system ; USER=root ; COMMAND=/bin/journalctl -xe
Jan 27 02:10:53 localhost.localdomain sudo[3789]: pam_systemd(sudo:session): Cannot create session: Already running in a session or user slice
1
SELinux is preventing /usr/lib/systemd/systemd from execute access on the file touchscreen.

可以看到,是 SELinux 阻止了应用程序。

1
2
3
4
5
6
7
8
9
10
11
12

[root@localhost system]# ^C
[root@localhost system]# getenforce
Enforcing
[root@localhost system]# sudo setenforce 0
[root@localhost system]#
[root@localhost system]#
[root@localhost system]#
[root@localhost system]#
[root@localhost system]#
[root@localhost system]# getenforce
Permissive
1
[root@localhost system]# systemctl daemon-reload
1
[root@localhost system]# sudo systemctl stop touchscreen
1
[root@localhost system]# sudo systemctl start touchscreen
1
[root@localhost system]# sudo systemctl status touchscreen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[root@localhost system]# systemctl daemon-reload
[root@localhost system]# sudo systemctl stop touchscreen
[root@localhost system]# sudo systemctl start touchscreen
[root@localhost system]# sudo systemctl status touchscreen
● touchscreen.service - 触摸屏
Loaded: loaded (/etc/systemd/system/touchscreen.service; disabled; vendor preset: disabled)
Active: active (running) since Tue 2026-01-27 02:14:00 PST; 3s ago
Main PID: 4026 (touchscreen)
Tasks: 2 (limit: 11231)
Memory: 27.7M
CGroup: /system.slice/touchscreen.service
├─4026 /home/memory/touchScreen/src/dist/touchscreen
└─4029 /home/memory/touchScreen/src/dist/touchscreen

Jan 27 02:14:00 localhost.localdomain touchscreen[4026]: 2026-01-27 02:14:00,905 - INFO - 更新控件 t19: 太原公司
Jan 27 02:14:00 localhost.localdomain touchscreen[4026]: 2026-01-27 02:14:00,916 - INFO - 更新控件 t20: 太原路段
Jan 27 02:14:00 localhost.localdomain touchscreen[4026]: 2026-01-27 02:14:00,927 - INFO - 更新控件 t21: 太原收费站
Jan 27 02:14:00 localhost.localdomain touchscreen[4026]: 2026-01-27 02:14:00,927 - INFO - ----------智能联网监测终端启动成功----------
Jan 27 02:14:00 localhost.localdomain touchscreen[4026]: 2026-01-27 02:14:00,927 - INFO - 初始化配置页1
Jan 27 02:14:00 localhost.localdomain touchscreen[4026]: 2026-01-27 02:14:00,927 - INFO - 初始化配置页1完毕
Jan 27 02:14:01 localhost.localdomain touchscreen[4026]: 2026-01-27 02:14:01,020 - INFO - ✅ 已连接到MySQL数据库: 14.103.238.145:10060/sx_rmgt
Jan 27 02:14:01 localhost.localdomain touchscreen[4026]: 2026-01-27 02:14:01,049 - INFO - 🔌 MySQL连接已关闭
Jan 27 02:14:01 localhost.localdomain touchscreen[4026]: 2026-01-27 02:14:01,049 - INFO - 获取部门名称: ['长治北收费站', '潞城收费站', '黎城收费站', '潞 >
Jan 27 02:14:01 localhost.localdomain touchscreen[4026]: 2026-01-27 02:14:01,049 - INFO - 获取部门ID: [181, 182, 191, 192, 193, 194, 195, 195, 196, 197, >

成功运行,设置开机自启。

CentOS

2026 年 1 月 26 日

Download - The CentOS Project

Centos8 下载详细步骤(国内镜像源)_centos8镜像下载-CSDN博客

官网只提供 CentOS9,CentOS10下载。

阿里巴巴开源镜像站-OPSX镜像站-阿里云开发者社区

吃饭。

1
sudo mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup
1
sudo curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-8.repo
1
sudo yum clean all
1
sudo yum makecache
1
sudo yum update
1
yum repolist

是不是因为下载游戏网络不稳定啊。

阿里云镜像源,两个虚拟机都出现这问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
[root@localhost ~]# sudo yum makecache
Loaded plugins: fastestmirror, langpacks
Determining fastest mirrors
Could not retrieve mirrorlist http://mirrorlist.centos.org?arch=x86_64&release=7&repo=sclo-rh error was
14: curl#6 - "Could not resolve host: mirrorlist.centos.org; Unknown error"


One of the configured repositories failed (Unknown),
and yum doesn't have enough cached data to continue. At this point the only
safe thing yum can do is fail. There are a few ways to work "fix" this:

1. Contact the upstream for the repository and get them to fix the problem.

2. Reconfigure the baseurl/etc. for the repository, to point to a working
upstream. This is most often useful if you are using a newer
distribution release than is supported by the repository (and the
packages for the previous distribution release still work).

3. Run the command with the repository temporarily disabled
yum --disablerepo=<repoid> ...

4. Disable the repository permanently, so yum won't use it by default. Yum
will then just ignore the repository until you permanently enable it
again or use --enablerepo for temporary usage:

yum-config-manager --disable <repoid>
or
subscription-manager repos --disable=<repoid>

5. Configure the failing repository to be skipped, if it is unavailable.
Note that yum will try to contact the repo. when it runs most commands,
so will have to try and fail each time (and thus. yum will be be much
slower). If it is a very temporary problem though, this is often a nice
compromise:

yum-config-manager --save --setopt=<repoid>.skip_if_unavailable=true

Cannot find a valid baseurl for repo: centos-sclo-rh/x86_64

清华大学 TUNA 源(推荐)

1
2
3
4
5
6
7
8
# 备份原文件
sudo mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup

# CentOS 7
sudo curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.tuna.tsinghua.edu.cn/repo/Centos-7.repo

# CentOS 8
sudo curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.tuna.tsinghua.edu.cn/repo/Centos-8.repo

解决 CentOS 7 报错: “Repository base is listed more than once in the configuration“问题-CSDN博客

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

[root@localhost etc]# cd yum.repos.d/
[root@localhost yum.repos.d]# ll
total 64
drwxr-xr-x. 2 root root 187 Jan 23 00:05 backup
-rw-r--r--. 1 root root 1694 Jan 26 00:03 CentOS-Base.repo
-rw-r--r--. 1 root root 153 Jan 26 00:01 CentOS-Base.repo.backup
-rw-r--r--. 1 root root 1309 May 21 2024 CentOS-CR.repo
-rw-r--r--. 1 root root 649 May 21 2024 CentOS-Debuginfo.repo
-rw-r--r--. 1 root root 314 May 21 2024 CentOS-fasttrack.repo
-rw-r--r--. 1 root root 630 May 21 2024 CentOS-Media.repo
-rw-r--r--. 1 root root 998 Dec 11 2018 CentOS-SCLo-scl.repo
-rw-r--r--. 1 root root 971 Oct 29 2018 CentOS-SCLo-scl-rh.repo
-rw-r--r--. 1 root root 1331 May 21 2024 CentOS-Sources.repo
-rw-r--r--. 1 root root 9454 May 21 2024 CentOS-Vault.repo
-rw-r--r--. 1 root root 616 May 21 2024 CentOS-x86_64-kernel.repo
-rw-r--r--. 1 root root 1358 Sep 4 2021 epel.repo
-rw-r--r--. 1 root root 1457 Sep 4 2021 epel-testing.repo
-rw-r--r--. 1 root root 474 Apr 24 2018 nodesource-el7.repo

立即禁用有问题的仓库

1
2
3
# 直接重命名有问题的仓库文件
sudo mv CentOS-SCLo-scl.repo CentOS-SCLo-scl.repo.disabled
sudo mv CentOS-SCLo-scl-rh.repo CentOS-SCLo-scl-rh.repo.disabled

清理缓存并测试

1
2
3
4
5
6
# 清理缓存
sudo yum clean all
sudo rm -rf /var/cache/yum/*

# 测试 yum
sudo yum makecache

EPEL(Extra Packages for Enterprise Linux)是由Fedora社区维护的一个高质量开源软件包仓库,专为基于Red Hat Enterprise Linux(RHEL)及其衍生发行版(如CentOS、Rocky Linux、AlmaLinux等)设计。

CentOS 8。

1
2
3
4
CentOS Linux 8 - AppStream                                                                                                                   0.0  B/s |   0  B     00:00
Errors during downloading metadata for repository 'appstream':
- Curl error (6): Couldn't resolve host name for http://mirrorlist.centos.org/?release=8&arch=x86_64&repo=AppStream&infra=stock [Could not resolve host: mirrorlist.centos.org]
Error: Failed to download metadata for repo 'appstream': Cannot prepare internal mirrorlist: Curl error (6): Couldn't resolve host name for http://mirrorlist.centos.org/?release=8&arch=x86_64&repo=AppStream&infra=stock [Could not resolve host: mirrorlist.centos.org]

这个错误的核心原因是:CentOS 8 官方源已经彻底停止服务了!

根本原因:

  1. CentOS 8 已于 2021年12月31日停止维护
  2. 官方镜像站 mirrorlist.centos.org已移除 CentOS 8 的软件包
  3. 所有指向这个地址的仓库都会失败

立即操作:

1
2
3
4
# 1. 禁用所有使用官方镜像的仓库
cd /etc/yum.repos.d
sudo mkdir -p backup
sudo mv CentOS-Linux-*.repo backup/

改用国内镜像站(推荐清华源)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 删除原有配置
sudo rm -f /etc/yum.repos.d/CentOS-*

# 使用清华大学的 CentOS 8 归档镜像
sudo tee /etc/yum.repos.d/CentOS-Vault.repo << 'EOF'
[baseos]
name=CentOS-8 - BaseOS (Tsinghua)
baseurl=https://mirrors.tuna.tsinghua.edu.cn/centos-vault/8.5.2111/BaseOS/$basearch/os/
gpgcheck=0
enabled=1

[appstream]
name=CentOS-8 - AppStream (Tsinghua)
baseurl=https://mirrors.tuna.tsinghua.edu.cn/centos-vault/8.5.2111/AppStream/$basearch/os/
gpgcheck=0
enabled=1
EOF'

强制清理缓存并重建

1
2
3
sudo dnf clean all
sudo rm -rf /var/cache/dnf/*
sudo dnf makecache

Python Release Python 3.6.8 | Python.org

下载这个包就行了:Windows x86-64 executable installer,以下是本机 Python 环境变量路径,添加在了用户环境变量下。

1
2
3
C:\WINDOWS\system32>where python
C:\Users\Lenovo\AppData\Local\Programs\Python\Python314\python.exe
C:\Users\Lenovo\AppData\Local\Microsoft\WindowsApps\python.exe

pychram里面切换python版本方式_pycharm切换python版本-CSDN博客

VLC

2026 年 1 月 1 日

Open In VLC :: WebExtension.ORG

C++

2025 年 12 月 22 日

C++ Web 编程 | 菜鸟教程

perl_百度百科

1
Perl语言是一种解释型、动态类型的编程语言,最初由Larry Wall于1987年开发。Perl语言的主要目标是提供一种灵活、高效、易于使用的文本处理工具,因此Perl语言的语法和特性都与文本处理相关。Perl语言被广泛应用于Web开发、系统管理、网络编程、数据处理、科学计算和人工智能等领域。

什么是Perl_Perl简介_Perl的优势以及应用场景-腾讯云开发者社区

Perl 环境安装 | 菜鸟教程

中天中鼎

2025 年 12 月 22 日

河南交投集团“高速公路+产业”落子布局、全面启动—中天中鼎研发产品生产基地揭牌_河南省人民政府国有资产监督管理委员会

中天中鼎研发产品生产基地。

1
该自助发卡缴费设备由中天公司“中天中鼎”研发生产基地制造生产,自助发卡机采用客货上下双工位设计,具备自助发卡、按键求助、智能语音交互等功能,可同时容纳1000张CPC卡,是常规发卡设备的两倍,发卡速度平均2s,可有效提升发卡效率10%。自助缴费机采用投卡式双工位设计,具备扫码移动支付、电子发票、特情处理等功能,可有效提升车道通行缴费效率10%。此次是“中天中鼎”自助发卡缴费机器人在传统收费站的首例应用,有效提升收费站车道智能化水平及通行效率,实现“收费精准化”“降低运营成本”等效果。(中天公司)

中天公司研发的“中天中鼎”系列自助发卡缴费机器人实现传统收费站改造首例应用 - 河南交通投资集团

堡垒机

2025 年 12 月 22 日

IT知识百科:什么是堡垒机?-阿里云开发者社区

1
在实际应用中,三者常配合使用:VPN负责“安全接入内网”,防火墙负责“过滤非法流量”,堡垒机负责“管控接入后的资源访问”。

堡垒机是什么设备?一文读懂企业IT安全与运维的“核心守卫”

堡垒机是什么设备?一文读懂安全管理的核心中枢

显卡

2025 年 12 月 18 日

2025显卡十大品牌排行榜_显卡品牌前十名-中国品牌网

英伟达找了哪些下游的厂商造显卡?厂商名单全跟中国有关!

Nvidia 是造 GPU 芯片的,而国内这些厂商是造显卡的:华硕、技嘉、微星、七彩虹、影驰、索泰、映众、耕升、万丽……

AMD 是 CPU 还是显卡?一文分清:别再搞混它的 “双重身份”!

处理器

2025 年 12 月 18 日

2025CPU十大品牌排行榜_CPU品牌前十名-中国品牌网

ARM 和 x86 架构到底啥区别?手机电脑背后的 “隐形功臣”,一文讲透

ARM,X86,龙芯。

X86处理器技术_百度百科

英特尔® 处理器名称、编号和代次列表

1
英特尔的 CPU 命名

英特尔CPU命名详解:32个后缀名、8个系列,如12700F等_英特尔cpu型号详解-CSDN博客

英特尔家族的CPU总共有8个“系列”,分别是至强(Xeon)、至强融核(Xeon Phi)、酷睿(Core)、奔腾(Pentium)、赛扬(Celeron)、凌动(Atom)、安腾(itanium)、Quark™ SoC。

Intel® Core™ i7-1195G7就属于酷睿系列的CPU。

总体来说,酷睿、赛扬、奔腾属于消费级CPU,主要分布在台式机和笔记本电脑领域。

Quark SoC属于可穿戴式设备。

凌动属于手机、平板和低成本电脑上的CPU。

至强、至强融核、安腾属于服务器领域的CPU,至强融核是英特尔集成众核架构产品。

1
Intel 的酷睿系列(i3/i5/i7/i9)和 AMD 的锐龙系列(R3/R5/R7/R9),有什么本质上的区别?

选处理器纠结 AMD 还是 Intel?2025 避坑指南 + 场景适配,一看就懂!

英特尔和amd处理器哪个好?技术架构、性能表现与选购指南

AMD 是 CPU 还是显卡?一文分清:别再搞混它的 “双重身份”!

龙芯

2026 年 2 月 2 日

国产自主 CPU 芯片,由中科院计算所研发,现在由龙芯中科负责产业化。

龙芯开源社区

国产操作系统、麒麟操作系统——麒麟软件官方网站

飞腾、龙芯、鲲鹏国产 CPU。

银河麒麟,中标麒麟。

2025 年 12 月 18 日

LoongArch®

【知识— x86架构和arm架构有什么关系和区别】_x86有关系吗-CSDN博客

指令集架构:

1
2
x86: 复杂指令集计算机(CISC),指令集较为繁杂,包含大量的指令,执行每个指令的周期数不一定相等。
ARM: 精简指令集计算机(RISC),指令集相对简洁,执行每个指令的周期数相对较为一致。

ARM 和 x86 架构到底啥区别?手机电脑背后的 “隐形功臣”,一文讲透

英特尔®指令集扩展技术

1
中华人民共和国为取得技术独立,在龙芯处理器中排他性地使用 Linux。

Linux 简介 | 菜鸟教程

VMware 安装 Centos7 超详细过程 | 菜鸟教程

MobaXterm

2025 年 12 月 18 日

MobaXterm-MobaXterm远程终端管理软件-远程链接控工具-MobaXterm中文网站

进制

2025 年 12 月 11 日

1
\x48 为什么是 'H' 的十六进制表示

字母 ‘H’ 的 ASCII 码是:

  • 十进制:72
  • 二进制:0100 1000
  • 十六进制:0x48

在十六进制中:

  • 4 表示 4×16¹ = 4×16 = 64
  • 8 表示 8×16⁰ = 8×1 = 8
  • 64 + 8 = 72 ✅

ASCII 码表对应关系

1
2
3
4
5
6
字母  十进制 十六进制 二进制
--------------------------
'H' 72 0x48 01001000
'I' 73 0x49 01001001
'J' 74 0x4A 01001010
...

实际验证下。

1
2
3
4
# Python 验证
print(hex(ord('H'))) # 输出: 0x48
print(int('48', 16)) # 输出: 72
print(chr(0x48)) # 输出: H

在你的原始数据中:

  • 我们看到 \x48这样的表示形式
  • 这实际上是 Python 中 bytes 字面量的表示方式
  • 表示一个字节,其十六进制值为 0x48

Python

2025 年 12 月 12 日

Python调用java接口_python 使用json调取java接口-CSDN博客

python如何调用java接口 – PingCode

1
2
3
4
5
6
7
8
9
10
11
@RestController
@RequestMapping("/api")
public class TestController {
/**
* 测试接口
*/
@GetMapping("/test")
public String test() {
System.out.println("Hello World");
return "Hello World";
}

用 Java 实现服务端,写个简单的接口。

1
2
3
4
5
6
7
8
9
10
11
import requests


def main():
response = requests.get('http://localhost:8080/api/test')
print(response.status_code)
print(response.text)


if __name__ == '__main__':
main()

在 Python 中,可以使用requests库发送 HTTP 请求,调用 Java RESTful 服务,灵活性强,能够与任何支持HTTP协议的编程语言进行集成。

调用成功,这就基本实现了 Python 调用 Java 后台接口应用。

2025 年 12 月 3 日

Flask 教程 | 菜鸟教程

【超实用】3 分钟,教你用 Docker 部署一个 Python 应用!-腾讯云开发者社区-腾讯云

1
2
3
4
5
6
7
8
9
from flask import Flask
app = Flask(__name__)

@app.route('/')
def index():
return "Hello World!"

if __name__ == '__main__':
app.run(host="0.0.0.0", port=5000,debug=True)

显而易见,就是一个非常基础的项目,当我们在浏览器输入网址以及对应的端口号的时候,会返回“Hello World!”内容。

执行以下命令安装可能缺失的依赖。

1
pip install Flask

运行成功,访问成功。

1
2
3
4
5
6
7
C:\Users\Lenovo\AppData\Local\Programs\Python\Python314\python.exe D:\Project\python\python通讯\python测试\test1203.py 
* Serving Flask app 'test1203'
* Debug mode: on
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on all addresses (0.0.0.0)
* Running on http://127.0.0.1:5000
* Running on http://192.168.1.105:5000

Python Flask 项目实战_python flask项目-CSDN博客

Flask 教程 | 菜鸟教程

1
2
3
4
5
6
7
8
9
10
FROM python:3.8-slim

WORKDIR /app

COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt

COPY . .

CMD ["gunicorn", "-b", "0.0.0.0:8000", "app:app"]
1
2
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt

触摸屏

2026 年 3 月 6 日

注:进度条控件和滑块控件关联使用时,进度条控件的ID一定要与滑块控件的ID相邻连续,且进度条的控件ID小于滑块控件ID,此时两者才能关联使用。如例程中:进度条控件ID为“1”,滑块控件ID为“2”。

2026 年 3 月 5 日

这么长时间了,这小屏还没有设计出页面来,光罗列了功能清单,得赶快设计下简单的排版,过两天得对一下。

2026 年 2 月 26 日

整理大彩串口屏,功能清单。

1
现在接手了一个任务,基于M系列、医用级、4.6寸、320*960、带组态RS232/TTL、LUA脚本、视频、IPS贴合的大彩串口屏,开发一套软件。这个串口屏是镶嵌在一台机顶盒上的,这个盒子将来要放在高速公路收费站上,连接收费网操作车道硬件的,包括ETC专道的费额显示器,自动栏杆机,牌识摄像头,车辆检测器等等,还有自助车道的小黄人车辆检测器,人工车道的称重设备等等,这是这台云控盒的应用场景。现在我要问的是,这台云控盒上的串口屏,应该提供些哪些功能呢,主要是数据展示功能,不需要复杂的业务逻辑,需要你帮忙设计下这个屏幕要显示些哪些功能模块,要不要分几个主页面展示,还有整体画幅内怎么安排合理,这些都是你要考虑的,给我个初步方案吧。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
raph TD
subgraph 首页 [960×320px 横屏]
A[顶部状态栏:960×60px] -->|左侧| 车道类型+时间<br>(如:ETC专道01 | 2026-02-26 14:30
A -->|右侧| 网络状态+告警提示<br>(收费网√ | 0条告警)
B[中部核心区:960×180px] --> B1[左模块:480×180px] --> 车道状态:通行中<br>今日通行:89辆(ETC占比92%)<br>当前车辆:京A12345 | 计费15.00
B --> B2[右模块:480×180px] --> 核心设备状态(图标+状态):<br>费显✓ 栏杆机✓ 牌识✓ 车检器✓<br>(异常设备标红,如:栏杆机×)
C[底部按钮区:960×80px] --> C1[左按钮:320×80px] --> 设备详情
C --> C2[中按钮:320×80px] --> 通行数据
C --> C3[右按钮:320×80px] --> 视频抓拍
end
style 首页 fill:#f5f5f5,stroke:#333,stroke-width:1px
style A fill:#1E3A8A,stroke:#fff,color:#fff
style B1 fill:#fff,stroke:#e0e0e0,color:#333
style B2 fill:#fff,stroke:#e0e0e0,color:#333
style C1 fill:#0EA5E9,stroke:#fff,color:#fff
style C2 fill:#10B981,stroke:#fff,color:#fff
style C3 fill:#F59E0B,stroke:#fff,color:#fff
1
2
3
4
5
帮我生成图片:这个首页图确实有点想法,我提几点意见:
1、背景图的话将来实拍一张收费站全景图就行,这个思路不错。
2、画面尺寸一定要是横屏960X320的,横竖比例是31,这一点要注意了,所有的画面统一尺寸。
3、首页显示的画面,应该要包括收费站信息,车道信息,放在如图所示居中的窗口顶部就行,时间显示的话可以放在屏幕画面右上角,将来所有页面统一,风格的话你自己敲定。
4、居中窗口的内容暂时先保留当前的设计,下面三个子页面按钮可以保留,如果可以的话也能扩充成四个子页面,但具体显示内容还需要斟酌。

Linux 系统中添加或追加环境变量_linux添加环境变量-CSDN博客

2026 年 2 月 25 日

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
local progress = 0
local meter_value = 0
local meter_flag = 0
local Progress_screenid = 5
local Meter_screenid = 6
local Slider_screendid = 7
local Seltor_screenid = 12
local Languagescreen_id = 14
local Bootanimation_id = 26
local changbit = 0

function on_init()
set_button_notify_release()
start_timer(0, 1000, 0, 0)
start_timer(2, 10, 0, 0)
local language_id = get_language()
if language_id == 0 then
set_value(Languagescreen_id,1,1)
set_value(Languagescreen_id,2,0)
else
set_value(Languagescreen_id,1,0)
set_value(Languagescreen_id,2,1)
end
end

lua 脚本就是在这里集中写的。

2026 年 2 月 24 日

今早刚到办公室,发现这台机子连续开机已经十多天了,如果期间无人操作的话说明之前盒子停机的问题出在软件程序上,也就是前段时间排查出的系统资源分配问题。

先不管了,研究下串口屏开发再说。

2026 年 2 月 9 日

最近两天应该没什么事,上周新发来的云控盒早都装好软件放太原南去试运行了,除非最近再发来几个新盒子。

快过年了,还发个什么盒子,就是有事也不揽。

2026 年 2 月 6 日

串口屏基本不支持TCP,不能直接进行网络通信,如果要远程连接数据库那只能在主机用Python程序,屏内操作逻辑用lua脚本或c实现。

串口屏只用来发送信号的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[Unit]
Description=触摸屏
After=network.target serial-getty@ttyS0.service
Wants=serial-getty@ttyS0.service

[Service]
# 日志追踪
Type=simple
User=root
WorkingDirectory=/home/memory/touchScreen/src/dist
ExecStart=/home/memory/touchScreen/src/dist/touchscreen
Restart=always
RestartSec=10
StandardOutput=journal+console
StandardError=journal+console
SyslogIdentifier=touchscreen
LogLevelMax=info

[Install]
WantedBy=multi-user.target
1
2
3
4
5
6
7
# 1. 重载systemd配置(修改service文件后必须执行)
systemctl daemon-reload
# 2. 重启服务(先停止再启动,确保配置生效)
systemctl stop touchscreen.service
systemctl start touchscreen.service
# 3. 查看状态+日志(此时就能看到程序的日志了)
systemctl status touchscreen.service

服务单元启动报错。

1
2
3
4
5
6
[root@localhost dist]# systemctl status touchscreen.service
● touchscreen.service - 触摸屏
Loaded: loaded (/etc/systemd/system/touchscreen.service; enabled; vendor preset: disabled)
Active: activating (auto-restart) (Result: exit-code) since Thu 2026-02-05 17:41:19 PST; 4s ago
Process: 3522 ExecStart=/home/memory/touchScreen/src/dist/touchscreen (code=exited, status=203/EXEC)
Main PID: 3522 (code=exited, status=203/EXEC)
1
2
# 模拟systemd的执行方式:根用户、无终端环境变量、绝对路径执行
su - root -c "/home/memory/touchScreen/src/dist/touchscreen"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[root@localhost src]# vi /etc/systemd/system/touchscreen.service
[Unit]
Description=触摸屏
After=network.target

[Service]
Type=simple
User=root
Environment="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
WorkingDirectory=/home/memory/touchScreen/src/dist
ExecStart=/home/memory/touchScreen/src/dist/touchscreen
Restart=always
RestartSec=10

StandardOutput=journal+file:/home/memory/touchScreen/src/log/touchscreen.log
StandardError=journal+file:/home/memory/touchScreen/src/log/touchscreen_error.log

SyslogIdentifier=touchscreen
LogLevelMax=info

[Install]
WantedBy=multi-user.target

这么配置有问题,现在启动后日志输出正常,但日志保存不起作用了

2026 年 2 月 4 日

最近几天的调试内容,都记录在Word文档里了,图片存储比较直观方便些。

从今天开始,研究大彩串口屏,这个屏幕比我想象的要高级许多。

2026 年 2 月 3 日

1
root@loongson-pc:/opt/scaner# sudo ifconfig enp3s0 192.168.2.34 netmask 255.255.255.0 up
1
2
3
4
5
6
7
8
9
10
11
# 配置 enp3s0 网卡
auto enp3s0
iface enp3s0 inet static
# 静态IP地址(按需修改为 192.168.2.12 或 192.168.2.34)
address 192.168.2.12
# 子网掩码
netmask 255.255.255.0
# 默认网关(必须填,和原来一致)
gateway 192.168.2.1
# DNS服务器(可选,填公共DNS或内网DNS)
dns-nameservers 223.5.5.5 8.8.8.8

2026 年 2 月 2 日

Redis 连接不上了,还没有完善从 Redis 拿 Token 再携带进每个请求中去的逻辑。

Smallpdf.com - A Free Solution to all your PDF Problems

免费在线编辑 PDF 文件的网站,订阅了7天免费试用,用完了就扔掉取消订阅不管了。

新发来的IP盒子串口屏厂家都不一样,尺寸也不一样,对应的上机位开发工具也不一样。

VisualTFT_开发软件_广州大彩专注组态串口屏,国内首创《虚拟串口屏》,大彩屏无需购买硬件!主营串口屏,串口显示屏,86面板,串口彩屏,触摸屏,HMI人机界面

D盘都爆满了,存储的图片视频过于庞大,这些资料应该寄存在U盘里,正好上个月从河南回来顺走了两块U盘,容量还不小。

代价就是两根顶好的网线了。

jiang让我买个大彩型号串口屏。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
01:35:01.103 [background-preinit] INFO  o.h.v.i.util.Version - [<clinit>,21] - [] - HV000001: Hibernate Validator 6.1.6.Final
01:35:02.185 [main] ERROR c.a.c.n.NacosConfigManager - [createConfigService,60] - [] - java.lang.reflect.InvocationTargetException
01:35:02.187 [main] WARN o.s.c.a.AnnotationConfigApplicationContext - [refresh,559] - [] - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration': Unsatisfied dependency expressed through field 'propertySourceLocators'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'nacosPropertySourceLocator' defined in class path resource [com/alibaba/cloud/nacos/NacosConfigBootstrapConfiguration.class]: Unsatisfied dependency expressed through method 'nacosPropertySourceLocator' parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'nacosConfigManager' defined in class path resource [com/alibaba/cloud/nacos/NacosConfigBootstrapConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.alibaba.cloud.nacos.NacosConfigManager]: Factory method 'nacosConfigManager' threw exception; nested exception is com.alibaba.cloud.nacos.diagnostics.analyzer.NacosConnectionFailureException: java.lang.reflect.InvocationTargetException
01:35:02.253 [main] ERROR o.s.b.d.LoggingFailureAnalysisReporter - [report,40] - [] -

***************************
APPLICATION FAILED TO START
***************************

Description:

Application failed to connect to Nacos server: "nacos-svc:8848"

Action:

Please check your Nacos server config

01:35:02.263 [main] ERROR o.s.b.SpringApplication - [reportFailure,837] - [] - Application run failed
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration': Unsatisfied dependency expressed through field 'propertySourceLocators'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'nacosPropertySourceLocator' defined in class path resource [com/alibaba/cloud/nacos/NacosConfigBootstrapConfiguration.class]: Unsatisfied dependency expressed through method 'nacosPropertySourceLocator' parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'nacosConfigManager' defined in class path resource [com/alibaba/cloud/nacos/NacosConfigBootstrapConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.alibaba.cloud.nacos.NacosConfigManager]: Factory method 'nacosConfigManager' threw exception; nested exception is com.alibaba.cloud.nacos.diagnostics.analyzer.NacosConnectionFailureException: java.lang.reflect.InvocationTargetException
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:643)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:130)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:399)

1
2
3
root@loongson-pc:/opt/scaner# grep -n "/opt/scaner/lib/" start.sh
80:cd /opt/scaner/lib/
108:cd /opt/scaner/lib/

2026 年 1 月 30 日

OpenAPI definition

1
2
3
4
5
6
7
8
9
10
/**
* 登录系统
*
* @param loginUser 登录用户信息
*/
public static void login(LoginUser loginUser) {
SaHolder.getStorage().set(LOGIN_USER_KEY, loginUser);
StpUtil.login(loginUser.getLoginId());
setLoginUser(loginUser);
}
1
2
3
http://101.227.48.102:10003
admin
yxjcgly202407!!AA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 登录方法
*
* @param loginBody 登录信息
* @return 结果
*/
@Anonymous
@PostMapping("/login")
public R<Map<String, Object>> login(@Validated @RequestBody LoginBody loginBody) {
Map<String, Object> ajax = new HashMap<>();
HttpServletRequest request = ServletUtils.getRequest();
UserAgent userAgent = UserAgentUtil.parse(request.getHeader("User-Agent"));
// if (!StringUtils.containsAnyIgnoreCase(userAgent.getOs().getName(), "Android", "iPhone")) {
String s = RsaUtilClient.decryptByPrivateKey(loginBody.getPassword());
loginBody.setPassword(s);
1
java.lang.IllegalArgumentException: Last unit does not have enough valid bits
1
pip install pycryptodome
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* 通过私钥解密
*
* @param data 加密的byte数组
* @param privateKey 私钥
* @return byte[]
* @author compass
* @date 2022/9/1 17:14
* @since 1.0.0
**/
public static byte[] decryptByPrivateKey(byte[] data, String privateKey)
throws Exception {
// 对密钥解密
byte[] keyBytes = decryptBASE64(privateKey);
// 取得私钥
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key privatizationKey = keyFactory.generatePrivate(pkcs8KeySpec);
// 对数据解密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, privatizationKey);
return cipher.doFinal(data);

}

这段代码的核心是用 RSA 私钥解密加密字节数组,流程为「BASE64 解私钥 → 构建 PKCS8 私钥对象 → 初始化解密器 → 执行解密」。

关键依赖:BASE64 解码工具、RSA 算法(KEY_ALGORITHM常量)、PKCS8 私钥格式。

核心类:PKCS8EncodedKeySpec(私钥包装)、KeyFactory(密钥转换)、Cipher(执行解密)。

1
2
3
private static final String privateKey = "MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHyZfSsYourNxaY7Nt+PrgrxkiA50efORdI5U5lsW79MmFnusUA355oaSXcLhu5xxB38SMSyP2KvuKNPuH3owIDAQABAkAfoiLyL+Z4lf4Myxk6xUDgLaWGximj20CUf+5BKKnlrK+Ed8gAkM0HqoTt2UZwA5E2MzS4EI2gjfQhz5X28uqxAiEA3wNFxfrCZlSZHb0gn2zDpWowcSxQAgiCstxGUoOqlW8CIQDDOerGKH5OmCJ4Z21v+F25WaHYPxCFMvwxpcw99EcvDQIgIdhDTIqD2jfYjPTY8Jj3EDGPbH2HHuffvflECt3Ek60CIQCFRlCkHpi7hthhYhovyloRYsM+IS9h/0BzlEAuO0ktMQIgSPT3aFAgJYwKpqRYKlLDVcflZFCKY7u3UP8iWi1Qw0Y=";

private static final String publicKey = "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ==";

后台的解密逻辑对应着前端 / 客户端必须执行的操作,缺一不可:

  1. 客户端获取后台提供的公钥(即你给出的publicKey字符串)。
  2. 客户端对明文密码执行「RSA 公钥加密」,得到加密后的字节数组。
  3. 客户端对该字节数组执行「BASE64 编码」,得到可传输的字符串data
  4. 客户端将data通过接口传输给后台。
  5. 后台接收data后,先执行「BASE64 解码」(对应代码中的decryptBASE64),得到加密字节数组。
  6. 后台再执行「RSA 私钥解密」,得到明文密码。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 加密<br>
* 用公钥加密
*
* @param data
* @param key
* @return
* @throws Exception
*/
public static byte[] encryptByPublicKey(String data, String key)
throws Exception {
// 对公钥解密
byte[] keyBytes = decryptBASE64(key);
// 取得公钥
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key publicKey = keyFactory.generatePublic(x509KeySpec);
// 对数据加密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(data.getBytes());
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 加密<br>
* 用私钥加密
*
* @param data
* @param key
* @return
* @throws Exception
*/
public static byte[] encryptByPrivateKey(byte[] data, String key)
throws Exception {
// 对密钥解密
byte[] keyBytes = decryptBASE64(key);
// 取得私钥
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
// 对数据加密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
return cipher.doFinal(data);
}
1
2
String base64 = encryptBASE64(encryptByPublicKey("yxjcgly202407!!AA", publicKey));
System.out.println(base64);
1
2
3
4
5
6
{
"username": "admin",
"password": "f9PPJgmZzDTJpNS+xCLWt3BDTXxIuHiH4InCmf0i05WbFZ2I5tkRQqENSLTzCRuAOsxXuGCw+qUkzg+wzeWKZg==",
"code": "",
"uuid": ""
}
1
2
3
4
5
6
7
{
"code": 500,
"msg": "验证码已失效",
"data": null,
"error": true,
"success": false
}

2026 年 1 月 29 日

1
2
3
4
5
6
7
8
9
@DataPermission(value = {
@DataRule(
tableName = "asset_info",
columnName = "dept_id",
matchRule = "IN"
)
},
logicJoin = "and")
List<AssetInfo> selectByExample(AssetInfoExample example);
1
2
3
4
5
6
7
8
9
10
11
12
AssetInfoExample example = new AssetInfoExample();
AssetInfoExample.Criteria criteria = example.createCriteria();
criteria.andAssetIpEqualTo(s)
.andIsDeleteEqualTo(MgtMagicValue.ZERO_BYTE);
List<AssetInfo> assetInfos = assetInfoService.selectByExample(example);
log.info("查询概览参数,获取本机资产数据结果:{}", JsonUtils.toJsonString(assetInfos));
if (CollUtil.isEmpty(assetInfos)) {
throw new ServiceException("获取资产信息有误");
}
if (assetInfos.size() > RmgtMagicValue.ONE) {
log.error("小屏概览,查询本服务器资源占用,获取路段服务器存在多条数据,请检查");
}

概览页数据高度,数字显示。

pic-刷图指令 — 淘晶驰串口屏资料中心 1.1.0-2026-01-16 10:28:24 文档

move-控件移动 — 淘晶驰串口屏资料中心 1.1.0-2026-01-16 10:28:24 文档

1
2
3
4
5
6
7
8
9
10
# 提取 data 下的参数值
cpu = data_dict['data']['cpu']
ram = data_dict['data']['ram']
disk = data_dict['data']['disk']

# 进度条数据
cpu_usage = int(float(cpu))
memory_usage = int(float(ram))
disk_usage = int(float(disk))
logger.info(f"cpu_usage: {cpu_usage}, memory_usage: {memory_usage}, disk_usage: {disk_usage}")
1
2
//查询配置的路段主机ip
String s = sysConfigService.selectConfigByKey("server_ip");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 根据键名查询参数配置信息
*
* @param configKey 参数key
* @return 参数键值
*/
@Cacheable(cacheNames = CacheNames.SYS_CONFIG, key = "#configKey")
@Override
public String selectConfigByKey(String configKey) {
SysConfig retConfig = baseMapper.selectOne(new LambdaQueryWrapper<SysConfig>()
.eq(SysConfig::getConfigKey, configKey));
if (ObjectUtil.isNotNull(retConfig)) {
return retConfig.getConfigValue();
}
return StringUtils.EMPTY;
}

@Cacheable(cacheNames = CacheNames.SYS_CONFIG, key = "#configKey")(核心,缓存注解)

这是 Spring Cache 提供的缓存注解,作用是:

在方法执行前,先去指定缓存中查询数据,如果缓存中有,直接返回缓存结果,不执行方法体内部的代码;如果缓存中没有,才执行方法体代码,并且将方法返回结果存入缓存,供后续相同请求使用。

概览页固定查询主机 ip 为 14.103.238.145 的系统参数(CPU,内存,磁盘)。

2025 年 1 月 28 日

1
git clone http://101.227.48.102:10080/zhm/tomm-rmgt-server.git
1
2
3
4
5
6
7
8
9
10
11
12
13
***************************
APPLICATION FAILED TO START
***************************

Description:

Failed to bind properties under 'spring.redis.password' to java.lang.String:

Reason: java.lang.IllegalStateException: either 'jasypt.encryptor.password' or one of ['jasypt.encryptor.private-key-string', 'jasypt.encryptor.private-key-location'] must be provided for Password-based or Asymmetric encryption

Action:

Update your application's configuration
1
2
3
4
5
6
7
8
9
10
# Spring配置
spring:
application:
name: ${ruoyi.name}
# 资源信息
messages:
# 国际化资源文件路径
basename: i18n/messages
profiles:
active: dev
1
rootaxdQ@1121.1226

访问令牌。

1
glpat-zx59RyfXdquQ2xBRmchz

2026 年 1 月 27 日

Nginx 烦人的 403 错误-腾讯云开发者社区-腾讯云

1
yum install zlib-devel
1
pip3 install pyinstaller
1
2
3
4
5
6
7
8
9
10
11
[root@localhost dist]# ./__init__
Traceback (most recent call last):
File "src/__init__.py", line 4, in <module>
File "PyInstaller/loader/pyimod03_importers.py", line 495, in exec_module
File "src/__init__.py", line 4, in <module>
File "PyInstaller/loader/pyimod03_importers.py", line 495, in exec_module
File "src/pages/home_page.py", line 6, in <module>
File "PyInstaller/loader/pyimod03_importers.py", line 495, in exec_module
File "src/utils/hmi_connect.py", line 3, in <module>
ModuleNotFoundError: No module named 'serial'
[3241] Failed to execute script '__init__' due to unhandled exception!
1
pip3 install pyserial
1
rm -rf dist build
1
pip3 install pymysql
1
pip3 install dataclasses
1
2026-01-26 17:36:24,358 - ERROR - 连接串口屏失败: [Errno 2] could not open port COM7: [Errno 2] No such file or directory: 'COM7'
1
ls -la /dev/ttyS* /dev/ttyUSB* 2>/dev/null
1
init = InitControl(HMIConnect(port='/dev/ttyS0', baudrate=115200))
1
2026-01-26 17:59:17,460 - ERROR - 连接串口屏失败: Could not configure port: (5, 'Input/output error')
1
pyinstaller --onefile __init__.py
1
2
3
4
[root@localhost dist]# ./__init__
2026-01-26 18:19:37,529 - INFO - 成功连接到串口屏 /dev/tty5
2026-01-26 18:19:37,540 - INFO - 已跳转到页面 0
2026-01-26 18:19:37,543 - ERROR - ❌ 接口调用失败: HTTPConnectionPool(host='localhost', port=10057): Max retries exceeded with url: /smallScreenDispla/homePageMonitorSummary (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x7f6930c08198>: Failed to establish a new connection: [Errno 111] Connection refused',))

虚拟机的串口和本机的串口的对应关系。

1
ls -la /dev/tty*
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
[root@localhost ~]# ls -la /dev/tty*
crw-rw-rw-. 1 root tty 5, 0 Jan 26 18:28 /dev/tty
crw--w----. 1 root tty 4, 0 Jan 26 18:28 /dev/tty0
crw--w----. 1 root tty 4, 1 Jan 26 18:28 /dev/tty1
crw--w----. 1 root tty 4, 10 Jan 26 18:28 /dev/tty10
crw--w----. 1 root tty 4, 11 Jan 26 18:28 /dev/tty11
crw--w----. 1 root tty 4, 12 Jan 26 18:28 /dev/tty12
crw--w----. 1 root tty 4, 13 Jan 26 18:28 /dev/tty13
crw--w----. 1 root tty 4, 14 Jan 26 18:28 /dev/tty14
crw--w----. 1 root tty 4, 15 Jan 26 18:28 /dev/tty15
crw--w----. 1 root tty 4, 16 Jan 26 18:28 /dev/tty16
crw--w----. 1 root tty 4, 17 Jan 26 18:28 /dev/tty17
crw--w----. 1 root tty 4, 18 Jan 26 18:28 /dev/tty18
crw--w----. 1 root tty 4, 19 Jan 26 18:28 /dev/tty19
crw--w----. 1 root tty 4, 2 Jan 26 18:28 /dev/tty2
crw--w----. 1 root tty 4, 20 Jan 26 18:28 /dev/tty20
crw--w----. 1 root tty 4, 21 Jan 26 18:28 /dev/tty21
crw--w----. 1 root tty 4, 22 Jan 26 18:28 /dev/tty22
crw--w----. 1 root tty 4, 23 Jan 26 18:28 /dev/tty23
crw--w----. 1 root tty 4, 24 Jan 26 18:28 /dev/tty24
crw--w----. 1 root tty 4, 25 Jan 26 18:28 /dev/tty25
crw--w----. 1 root tty 4, 26 Jan 26 18:28 /dev/tty26
crw--w----. 1 root tty 4, 27 Jan 26 18:28 /dev/tty27
crw--w----. 1 root tty 4, 28 Jan 26 18:28 /dev/tty28
crw--w----. 1 root tty 4, 29 Jan 26 18:28 /dev/tty29
crw--w----. 1 root tty 4, 3 Jan 26 18:28 /dev/tty3
crw--w----. 1 root tty 4, 30 Jan 26 18:28 /dev/tty30
crw--w----. 1 root tty 4, 31 Jan 26 18:28 /dev/tty31
crw--w----. 1 root tty 4, 32 Jan 26 18:28 /dev/tty32
crw--w----. 1 root tty 4, 33 Jan 26 18:28 /dev/tty33
crw--w----. 1 root tty 4, 34 Jan 26 18:28 /dev/tty34
crw--w----. 1 root tty 4, 35 Jan 26 18:28 /dev/tty35
crw--w----. 1 root tty 4, 36 Jan 26 18:28 /dev/tty36
crw--w----. 1 root tty 4, 37 Jan 26 18:28 /dev/tty37
crw--w----. 1 root tty 4, 38 Jan 26 18:28 /dev/tty38
crw--w----. 1 root tty 4, 39 Jan 26 18:28 /dev/tty39
crw--w----. 1 root tty 4, 4 Jan 26 18:28 /dev/tty4
crw--w----. 1 root tty 4, 40 Jan 26 18:28 /dev/tty40
crw--w----. 1 root tty 4, 41 Jan 26 18:28 /dev/tty41
crw--w----. 1 root tty 4, 42 Jan 26 18:28 /dev/tty42
crw--w----. 1 root tty 4, 43 Jan 26 18:28 /dev/tty43
crw--w----. 1 root tty 4, 44 Jan 26 18:28 /dev/tty44
crw--w----. 1 root tty 4, 45 Jan 26 18:28 /dev/tty45
crw--w----. 1 root tty 4, 46 Jan 26 18:28 /dev/tty46
crw--w----. 1 root tty 4, 47 Jan 26 18:28 /dev/tty47
crw--w----. 1 root tty 4, 48 Jan 26 18:28 /dev/tty48
crw--w----. 1 root tty 4, 49 Jan 26 18:28 /dev/tty49
crw--w----. 1 root tty 4, 5 Jan 26 18:28 /dev/tty5
crw--w----. 1 root tty 4, 50 Jan 26 18:28 /dev/tty50
crw--w----. 1 root tty 4, 51 Jan 26 18:28 /dev/tty51
crw--w----. 1 root tty 4, 52 Jan 26 18:28 /dev/tty52
crw--w----. 1 root tty 4, 53 Jan 26 18:28 /dev/tty53
crw--w----. 1 root tty 4, 54 Jan 26 18:28 /dev/tty54
crw--w----. 1 root tty 4, 55 Jan 26 18:28 /dev/tty55
crw--w----. 1 root tty 4, 56 Jan 26 18:28 /dev/tty56
crw--w----. 1 root tty 4, 57 Jan 26 18:28 /dev/tty57
crw--w----. 1 root tty 4, 58 Jan 26 18:28 /dev/tty58
crw--w----. 1 root tty 4, 59 Jan 26 18:28 /dev/tty59
crw--w----. 1 root tty 4, 6 Jan 26 18:28 /dev/tty6
crw--w----. 1 root tty 4, 60 Jan 26 18:28 /dev/tty60
crw--w----. 1 root tty 4, 61 Jan 26 18:28 /dev/tty61
crw--w----. 1 root tty 4, 62 Jan 26 18:28 /dev/tty62
crw--w----. 1 root tty 4, 63 Jan 26 18:28 /dev/tty63
crw--w----. 1 root tty 4, 7 Jan 26 18:28 /dev/tty7
crw--w----. 1 root tty 4, 8 Jan 26 18:28 /dev/tty8
crw--w----. 1 root tty 4, 9 Jan 26 18:28 /dev/tty9
crw-rw----. 1 root dialout 4, 64 Jan 26 18:28 /dev/ttyS0
crw-rw----. 1 root dialout 4, 65 Jan 26 18:28 /dev/ttyS1
crw-rw----. 1 root dialout 4, 66 Jan 26 18:28 /dev/ttyS2
crw-rw----. 1 root dialout 4, 67 Jan 26 18:28 /dev/ttyS3

虚拟控制台终端 (tty0-tty63)不可用于串口通信,是系统控制台。

物理串口 (ttyS0-ttyS3)这才是真正的串口设备,用于串口通信。

当前控制台 (tty)。

1
2
3
4
5
6
if __name__ == '__main__':
# 使用示例
# init = InitControl(HMIConnect(port='COM7', baudrate=115200))
# init = InitControl(HMIConnect(port='COM5', baudrate=115200))
init = InitControl(HMIConnect(port='/dev/ttyS0', baudrate=115200))
init.main()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@localhost dist]# ./__init__
2026-01-26 18:42:27,369 - INFO - 成功连接到串口屏 /dev/ttyS0
2026-01-26 18:42:27,381 - INFO - 已跳转到页面 0
2026-01-26 18:42:27,384 - ERROR - ❌ 接口调用失败: HTTPConnectionPool(host='localhost', port=10057): Max retries exceeded with url: /smallScreenDispla/homePageMonitorSummary (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x7f2a619b60f0>: Failed to establish a new connection: [Errno 111] Connection refused',))
2026-01-26 18:42:27,384 - WARNING - ⚠️ 接口调用失败,使用模拟数据
2026-01-26 18:42:27,384 - INFO - 获取系统数据成功
2026-01-26 18:42:27,395 - INFO - 更新控件 t13: 正常
2026-01-26 18:42:27,406 - INFO - 更新控件 t14: 4个
2026-01-26 18:42:27,418 - INFO - 更新控件 g1: 18:42
2026-01-26 18:42:27,429 - INFO - 更新控件 t16: 8个
2026-01-26 18:42:27,440 - INFO - 更新控件 t17: 0个
2026-01-26 18:42:27,451 - INFO - 更新控件 g0: 18:42
2026-01-26 18:42:27,462 - INFO - 更新控件 t19: 太原公司
2026-01-26 18:42:27,473 - INFO - 更新控件 t20: 太原路段
2026-01-26 18:42:27,485 - INFO - 更新控件 t21: 太原收费站
2026-01-26 18:42:27,485 - INFO - ----------智能联网监测终端启动成功----------

总算在 Linux 环境下部署成功了。

研究下怎么把这个服务设置成开机自启,注册成为系统服务的其中一个。

Linux系统服务神器:systemctl的简单配置与使用-腾讯云开发者社区-腾讯云

2026 年 1 月 26 日

安装 Pyinstaller 打包项目。

1
pip install pyinstaller
1
pyinstaller --onefile main.py

这行代码报错了,日志打印不规范,见鬼的是为什么现在才报错。

1
2
3
4
5
6
def validate(self, hex_data) -> tuple:
"""校验输入"""
commands = []
# 重置校验结果
try:
logger.info("hex_data:", hex_data)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
--- Logging error ---
Traceback (most recent call last):
File "C:\Users\Lenovo\AppData\Local\Programs\Python\Python314\Lib\logging\__init__.py", line 1151, in emit
msg = self.format(record)
File "C:\Users\Lenovo\AppData\Local\Programs\Python\Python314\Lib\logging\__init__.py", line 999, in format
return fmt.format(record)
~~~~~~~~~~^^^^^^^^
File "C:\Users\Lenovo\AppData\Local\Programs\Python\Python314\Lib\logging\__init__.py", line 712, in format
record.message = record.getMessage()
~~~~~~~~~~~~~~~~~^^
File "C:\Users\Lenovo\AppData\Local\Programs\Python\Python314\Lib\logging\__init__.py", line 400, in getMessage
msg = msg % self.args
~~~~^~~~~~~~~~~
TypeError: not all arguments converted during string formatting
Call stack:
File "D:\Project\luofeng\touchScreen\src\__init__.py", line 51, in <module>
init.main()
File "D:\Project\luofeng\touchScreen\src\__init__.py", line 33, in main
touch_event.display_touch_listener()
File "D:\Project\luofeng\touchScreen\src\utils\touch_event.py", line 51, in display_touch_listener
self.handle_validate_event()
File "D:\Project\luofeng\touchScreen\src\utils\touch_event.py", line 209, in handle_validate_event
result, commands, dept_id = self.config_page1_updater.validate(self.hex_data)
File "D:\Project\luofeng\touchScreen\src\pages\config_page_1.py", line 79, in validate
logger.info("hex_data:", hex_data)
Message: 'hex_data:'
Arguments: ('C0 E8 B3 C7 CA D5 B7 D1 D5 BE',)
2026-01-26 09:36:43,972 - INFO - 🔍 校验输入: 收费站=黎城收费站
2026-01-26 09:36:43,973 - INFO - 获取到dept_id: 191
2026-01-26 09:36:43,973 - INFO - ✅ 输入校验通过
2026-01-26 09:36:43,973 - INFO - 校验结束, 标记结果状态:True; 校验结果信息: ['t4.txt="校验通过\r\n点击下一步"', 't4.pco=1696']; 收费站dept_id: 191
2026-01-26 09:36:43,973 - INFO - ==================================================

重新安装yum。

1
sudo yum clean all
1
sudo yum reinstall yum

安装 python3 。

1
sudo yum install python3 python3-devel python3-pip
1
2
3
4
5
6
7
8
9
10
11

[root@localhost admin]# pip3 install pyinstaller
WARNING: Running pip install with root privileges is generally not a good idea. Try `pip3 install --user` instead.
Collecting pyinstaller
Downloading https://files.pythonhosted.org/packages/b0/e6/e5760666896739115b0e4538a42cdd895215581618ec885ad043dd35ee57/pyinstaller-4.10.tar.gz (2.7MB)
100% |████████████████████████████████| 2.7MB 557kB/s
Complete output from command python setup.py egg_info:
Error: Building wheels requires the 'wheel' package. Please `pip install wheel` then try again.

----------------------------------------
Command "python setup.py egg_info" failed with error code 1 in /tmp/pip-build-6j4h6vx1/pyinstaller/

这个报错是因为没有安装编译依赖。

1
2
3
4
5
6
7
8
9
10
11
# 1.1 安装gcc等编译工具
sudo yum install -y gcc gcc-c++ make

# 1.2 安装Python开发工具
sudo yum install -y python3-devel python3-setuptools python3-wheel

# 1.3 安装其他依赖
sudo yum install -y openssl-devel bzip2-devel libffi-devel sqlite-devel

# 1.4 安装pip工具
python3 -m pip install --upgrade pip setuptools wheel

添加 PATH 到环境变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
# 1.1 查看当前 PATH
echo $PATH

# 1.2 查找 pyinstaller 脚本
find /root/.local -name "pyinstaller" -type f
# 通常在这个位置:/root/.local/bin/pyinstaller

# 1.3 添加到 PATH(临时)
export PATH="/root/.local/bin:$PATH"

# 1.4 验证
which pyinstaller
pyinstaller --version
1
pyinstaller --onefile __init__.py
1
2
3
4
5
6
7
8
9
pyinstaller --onefile \
--name "myapp" \
--hidden-import requests \
--hidden-import chardet \
--hidden-import idna \
--hidden-import urllib3 \
--hidden-import certifi \
--add-data "src:src" \
src/__init__.py

还是报错。

1
2
3
4
5
6
7
8
9
[root@localhost dist]# ./myapp
Traceback (most recent call last):
File "src/__init__.py", line 4, in <module>
File "PyInstaller/loader/pyimod03_importers.py", line 495, in exec_module
File "src/__init__.py", line 4, in <module>
File "PyInstaller/loader/pyimod03_importers.py", line 495, in exec_module
File "src/pages/home_page.py", line 3, in <module>
ModuleNotFoundError: No module named 'requests'
[108286] Failed to execute script '__init__' due to unhandled exception!

排查问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
[root@localhost dist]# python3 -c "
> import pkg_resources
> pkgs = ['requests', 'urllib3', 'chardet', 'idna', 'certifi', 'charset-normalizer']
> for pkg in pkgs:
> try:
> dist = pkg_resources.get_distribution(pkg)
> print(f'✓ {pkg}: {dist.version}')
> except:
> print(f'✗ {pkg}: 未安装')
> "
✓ requests: 2.27.1
✓ urllib3: 1.26.20
✗ chardet: 未安装
✓ idna: 3.10
✓ certifi: 2025.4.26
✓ charset-normalizer: 2.0.12
[root@localhost dist]# ./myapp
Traceback (most recent call last):
File "src/__init__.py", line 4, in <module>
File "PyInstaller/loader/pyimod03_importers.py", line 495, in exec_module
File "src/__init__.py", line 4, in <module>
File "PyInstaller/loader/pyimod03_importers.py", line 495, in exec_module
File "src/pages/home_page.py", line 3, in <module>
ModuleNotFoundError: No module named 'requests'
[108410] Failed to execute script '__init__' due to unhandled exception!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 安装 chardet
pip install chardet

# 验证所有依赖
python3 -c "
import pkg_resources
pkgs = ['requests', 'urllib3', 'chardet', 'idna', 'certifi', 'charset-normalizer']
for pkg in pkgs:
try:
dist = pkg_resources.get_distribution(pkg)
print(f'✓ {pkg}: {dist.version}')
except:
print(f'✗ {pkg}: 未安装')
"

Python 3.14最新版本安装和环境配置-腾讯云开发者社区-腾讯云

官方镜像停止维护。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
[root@localhost dist]# sudo yum install python3.14 python3.14-pip
Loaded plugins: fastestmirror, langpacks
Loading mirror speeds from cached hostfile
Could not retrieve mirrorlist http://mirrorlist.centos.org?arch=x86_64&release=7&repo=sclo-rh error was
14: curl#6 - "Could not resolve host: mirrorlist.centos.org; Unknown error"


One of the configured repositories failed (Unknown),
and yum doesn't have enough cached data to continue. At this point the only
safe thing yum can do is fail. There are a few ways to work "fix" this:

1. Contact the upstream for the repository and get them to fix the problem.

2. Reconfigure the baseurl/etc. for the repository, to point to a working
upstream. This is most often useful if you are using a newer
distribution release than is supported by the repository (and the
packages for the previous distribution release still work).

3. Run the command with the repository temporarily disabled
yum --disablerepo=<repoid> ...

4. Disable the repository permanently, so yum won't use it by default. Yum
will then just ignore the repository until you permanently enable it
again or use --enablerepo for temporary usage:

yum-config-manager --disable <repoid>
or
subscription-manager repos --disable=<repoid>

5. Configure the failing repository to be skipped, if it is unavailable.
Note that yum will try to contact the repo. when it runs most commands,
so will have to try and fail each time (and thus. yum will be be much
slower). If it is a very temporary problem though, this is often a nice
compromise:

yum-config-manager --save --setopt=<repoid>.skip_if_unavailable=true

Cannot find a valid baseurl for repo: centos-sclo-rh/x86_64
[root@localhost dist]#

这段代码很关键。

1
2
3
4
5
6
7
8
9
try:
# 检查串口是否有数据
if _serial and _serial.in_waiting > 0:
# 获取原始指令信息
data = _serial.read(_serial.in_waiting)
time.sleep(0.01) # 10ms延迟
# 再读取剩余数据
data += _serial.read(_serial.in_waiting)
self.data = data

加上可能再次读取收费站信息,删掉就读不到完整的收费站信息,两头堵。

阿里云镜像源,两个虚拟机都出现这问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

[root@localhost ~]# sudo yum makecache
Loaded plugins: fastestmirror, langpacks
Determining fastest mirrors
Could not retrieve mirrorlist http://mirrorlist.centos.org?arch=x86_64&release=7&repo=sclo-rh error was
14: curl#6 - "Could not resolve host: mirrorlist.centos.org; Unknown error"


One of the configured repositories failed (Unknown),
and yum doesn't have enough cached data to continue. At this point the only
safe thing yum can do is fail. There are a few ways to work "fix" this:

1. Contact the upstream for the repository and get them to fix the problem.

2. Reconfigure the baseurl/etc. for the repository, to point to a working
upstream. This is most often useful if you are using a newer
distribution release than is supported by the repository (and the
packages for the previous distribution release still work).

3. Run the command with the repository temporarily disabled
yum --disablerepo=<repoid> ...

4. Disable the repository permanently, so yum won't use it by default. Yum
will then just ignore the repository until you permanently enable it
again or use --enablerepo for temporary usage:

yum-config-manager --disable <repoid>
or
subscription-manager repos --disable=<repoid>

5. Configure the failing repository to be skipped, if it is unavailable.
Note that yum will try to contact the repo. when it runs most commands,
so will have to try and fail each time (and thus. yum will be be much
slower). If it is a very temporary problem though, this is often a nice
compromise:

yum-config-manager --save --setopt=<repoid>.skip_if_unavailable=true

Cannot find a valid baseurl for repo: centos-sclo-rh/x86_64

清华大学 TUNA 源(推荐)

1
2
3
4
5
6
7
8
# 备份原文件
sudo mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup

# CentOS 7
sudo curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.tuna.tsinghua.edu.cn/repo/Centos-7.repo

# CentOS 8
sudo curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.tuna.tsinghua.edu.cn/repo/Centos-8.repo

解决 CentOS 7 报错: “Repository base is listed more than once in the configuration“问题-CSDN博客

立即禁用有问题的仓库

1
2
3
# 直接重命名有问题的仓库文件
sudo mv CentOS-SCLo-scl.repo CentOS-SCLo-scl.repo.disabled
sudo mv CentOS-SCLo-scl-rh.repo CentOS-SCLo-scl-rh.repo.disabled

清理缓存并测试

1
2
3
4
5
6
# 清理缓存
sudo yum clean all
sudo rm -rf /var/cache/yum/*

# 测试 yum
sudo yum makecache

2026 年 1 月 23 日

收费站智能终端。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<select id="selectByExample" parameterType="com.tomm.system.domain.ItemsExample" resultMap="BaseResultMap">
select
<if test="distinct">
distinct
</if>
<include refid="Base_Column_List"/>
from zabbix.items
<if test="_parameter != null">
<include refid="Example_Where_Clause"/>
</if>
<if test="orderByClause != null">
order by ${orderByClause}
</if>
<if test="limit != null">
<if test="offset != null">
limit ${offset}, ${limit}
</if>
<if test="offset == null">
limit ${limit}
</if>
</if>
</select>

获取部门名称和部门ID。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 获取部门名称和部门ID
def load_station_name_dept_id(self):
"""获取部门名称和部门ID"""
# 创建管理器
manager = MySQLManager(MySQLConfig())

# 执行查询
if manager.connect():
try:
station_name_dept_id = manager.execute_query(
f"SELECT DISTINCT station_name, dept_id FROM `t_section_host_dept_map`")
# 将查询结果存储为字典
for row in station_name_dept_id:
station_name = row.get('station_name')
dept_id = row.get('dept_id')
if station_name and dept_id:
self.result_dict[station_name] = dept_id
finally:
manager.close()

logger.info(f"获取部门名称和部门ID成功: {self.result_dict}")
logger.info(f"获取部门名称: {self.result_dict.keys()}")
logger.info(f"获取部门ID: {self.result_dict.values()}")
1
2
3
2026-01-23 09:47:06,174 - INFO - 获取部门名称和部门ID成功: {'长治北收费站': 181, '潞城收费站': 182, '黎城收费站': 191, '潞城东收费站': 192, '壶关收费站': 193, '西池收费站': 194, '长治东收费站': 195, '平顺收费站': 195, '虹霓峡收费站': 196, '长子北收费站': 197, '丰宜收费站': 198, '西井收费站': 198}

2026-01-23 09:47:06,174 - INFO - 获取部门名称: dict_keys(['长治北收费站', '潞城收费站', '黎城收费站', '潞城东收费站', '壶关收费站', '西池收费站', '长治东收费站', '平顺收费站', '虹霓峡收费站', '长子北收费站', '丰宜收费站', '西井收费站'])

这个列表在两个地方用到了。

1
2
logger.info(f"获取部门名称: {list(self.result_dict.keys())}")
logger.info(f"获取部门ID: {list(self.result_dict.values())}")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def select_dept(self):
"""选择部门"""
"""每次调用选择下一个收费站,循环选择"""
station_list = self.result_dict.keys()
if len(station_list) <= 1:
return # 没有可选的收费站

# 获取当前要选择的收费站
current_station = list(station_list)[self.current_index]
self.current_station = current_station
# 发送选择命令到HMI
self.hmi_connect.send_command(f'b2.txt="{current_station}"')
# 更新索引,循环到列表末尾时回到第一个收费站
self.current_index += 1
print(f"当前选择收费站: {current_station}")
if self.current_index >= len(station_list):
self.current_index = 1 # 跳过标题,从第一个收费站重新开始

# 如果需要,可以返回当前选择的收费站
return current_station
1
2
3
dept_id = self.result_dict.get(station)
# 校验通过,返回校验正确
logger.info(f"获取到dept_id: {dept_id}")

监测页也一样。

妈的,监测页没有这个需求,不用改了。

image-20260123105450584

【超实用】3 分钟,教你用 Docker 部署一个 Python 应用!-腾讯云开发者社区-腾讯云

最佳静态网站托管:3 个免费 + 2 个低成本选项 - HostScore

Vercel Pricing: Hobby, Pro, and Enterprise plans

2026 年 1 月 22 日

监测页同步更新。

控件添加,减少第8条记录,翻页功能,清除记录,全局索引,删除查找数据冗余代码。

1
2026-01-22 16:30:19,306 - ERROR - ❌ 更新概览页面失败: unsupported operand type(s) for /: 'str' and 'int'
1
2
3
4
5
6
7
8
9
10
@staticmethod
def update_progress_bars(data):
"""更新进度条控件以及对应文本控件"""
commands = [f"j1.val='{data['cpu_usage']}'", f"j2.val='{data['memory_usage']}'", f"j3.val='{data['disk_usage']}'"]
# 更新进度条值
# 更新进度条文本显示
# commands.append(f't2.txt="CPU{data["cpu_usage"]}%"')
# commands.append(f't3.txt="内存{data["memory_usage"]}%"')
# commands.append(f't4.txt="磁盘{data["disk_usage"]}%"')
return commands

2026 年 1 月 21 日

1
2
3
4
5
6
7
/**
* 根据指标id和指标值类型,查询所有类型指标值
* @param itemIds 指标id
* @param type 指标值类型
* @return 指标值集合
*/
List<LatestItemsHistoryVo> selectAllHistoryInfo(@Param("itemIds") List<Long> itemIds, @Param("type") Integer type);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
<select id="selectAllHistoryInfo" parameterType="map" resultType="com.tomm.system.domain.vo.LatestItemsHistoryVo">
SELECT
hu.itemid itemId,
ROUND(hu.`value`, 2) AS value,
hu.clock
FROM
<if test="type=='0'.toString()">
zabbix.history hu
JOIN (
SELECT
h.itemid,
MAX( h.clock ) AS clock
FROM
zabbix.history h
WHERE
h.itemid in
<foreach collection="itemIds" item="itemid" open="(" separator="," close=")">
#{itemid}
</foreach>
GROUP BY h.itemid
) a on hu.itemid=a.itemid and hu.clock=a.clock
</if>
<if test="type=='1'.toString()">
zabbix.history_str hu
JOIN (
SELECT
h.itemid,
MAX( h.clock ) AS clock
FROM
zabbix.history_str h
WHERE
h.itemid in
<foreach collection="itemIds" item="itemid" open="(" separator="," close=")">
#{itemid}
</foreach>
GROUP BY h.itemid
) a on hu.itemid=a.itemid and hu.clock=a.clock
</if>
<if test="type=='2'.toString()">
zabbix.history_log hu
JOIN (
SELECT
h.itemid,
MAX( h.clock ) AS clock
FROM
zabbix.history_log h
WHERE
h.itemid in
<foreach collection="itemIds" item="itemid" open="(" separator="," close=")">
#{itemid}
</foreach>
GROUP BY h.itemid
) a on hu.itemid=a.itemid and hu.clock=a.clock
</if>
<if test="type=='3'.toString()">
zabbix.history_uint hu
JOIN (
SELECT
h.itemid,
MAX( h.clock ) AS clock
FROM
zabbix.history_uint h
WHERE
h.itemid in
<foreach collection="itemIds" item="itemid" open="(" separator="," close=")">
#{itemid}
</foreach>
GROUP BY h.itemid
) a on hu.itemid=a.itemid and hu.clock=a.clock
</if>
<if test="type=='4'.toString()">
zabbix.history_text hu
JOIN (
SELECT
h.itemid,
MAX( h.clock ) AS clock
FROM
zabbix.history_text h
WHERE
h.itemid in
<foreach collection="itemIds" item="itemid" open="(" separator="," close=")">
#{itemid}
</foreach>
GROUP BY h.itemid
) a on hu.itemid=a.itemid and hu.clock=a.clock
</if>
GROUP BY hu.itemid
</select>
<select id="getDeviceIndexTop" resultType="com.tomm.system.domain.vo.DeviceIndexTopVo">
SELECT
ai.asset_name AS assetName,
i.itemid AS itemId,
i.name AS itemName,
ROUND(COALESCE(hu.`value`, 0.00), 2) AS value
FROM
asset_info ai
LEFT JOIN zabbix.items i
ON i.hostid = ai.host_id
AND i.name = #{itemName}
LEFT JOIN (
SELECT
itemid,
MAX(clock) AS clock
FROM
zabbix.history
GROUP BY
itemid
) h ON h.itemid = i.itemid
LEFT JOIN zabbix.history hu
ON hu.itemid = h.itemid
AND hu.clock = h.clock
WHERE
ai.host_id IN
<foreach collection="hostIds" item="hostId" open="(" separator="," close=")">
#{hostId}
</foreach>
ORDER BY
ROUND(COALESCE(hu.`value`, 0.00), 2) DESC
LIMIT 5;
</select>
  1. 查询目的

这个查询的目标是:获取多个监控项(item)的最新一条历史记录

具体来说:

  • 对每个 itemid,找到其最近的时间戳(MAX(clock)
  • 然后根据 itemid和时间戳关联回原表,获取该条记录的详细信息
  • 返回每个监控项的最新数值和时间
  1. 自连接分析

为什么需要自连接?

  1. 子查询(a)

    1
    2
    3
    4
    SELECT h.itemid, MAX(h.clock) AS clock
    FROM zabbix.history h
    WHERE h.itemid IN (...)
    GROUP BY h.itemid

    作用:找出每个监控项的最大时间戳(最新记录的时间)

  2. 主查询

    1
    2
    3
    SELECT hu.itemid, ROUND(hu.`value`, 2), hu.clock
    FROM zabbix.history hu
    JOIN a ON hu.itemid = a.itemid AND hu.clock = a.clock

    作用:用上面找到的 (itemid, 最大clock)关联回原表,获取具体的值

这实际上是两步操作

  1. 先找”每个监控项的最新时间”

  2. 再用”监控项+时间”去匹配具体的数据

  3. type 参数的意义

从代码看,Zabbix 有多个历史表:

  • history(type=’0’):浮点数数据
  • history_str(type=’1’):字符串数据
  • history_log(type=’2’):日志数据
  • history_uint(type=’3’):无符号整数
  • history_text(type=’4’):文本数据

type参数决定了查询哪个历史表,因为 Zabbix 根据数据类型将历史数据存储在不同的表中。

定义了查询列。

1
2
3
4
<sql id="Base_Column_List">
id ,road_name,station_name,host_ip,host_type,dept_id,sectionid,stationid,laneid,gantryid,show_item_id,
device_type,create_time,create_by,update_time,update_by,host_id
</sql>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<select id="selectSectionHostDeptMapByHostTypeAndDeptId" parameterType="java.lang.Byte" resultType="com.tomm.system.domain.SectionHostDeptMap">
SELECT
<include refid="Base_Column_List" />
FROM
t_section_host_dept_map
<where>
<if test="deptIds != null and deptIds.size() > 0">
AND dept_id IN
<foreach item="item" collection="deptIds" open="(" separator="," close=")">
#{item}
</foreach>
</if>
<if test="hostTypes != null and hostTypes.size() > 0">
AND host_type IN
<foreach item="item" collection="hostTypes" open="(" separator="," close=")">
#{item}
</foreach>
</if>
</where>
</select>
1
2
3
4
5
6
7
/**
* 根据条件查询指标集合
*
* @param itemDto 查询条件
* @return 指标集合
*/
List<Items> selectItemByExample(ItemDto itemDto);

四舍五入,保留小数位数。

1
2
3
4
5
SELECT
hu.itemid itemId,
ROUND(hu.`value`, 2) AS value,
hu.clock
FROM

根据指标id(itemId)和指标类型(type),查表 zabbix.history。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
SELECT
hu.itemid itemId,
ROUND(hu.`value`, 2) AS value,
hu.clock
FROM
<if test="type=='0'.toString()">
zabbix.history hu
JOIN (
SELECT
h.itemid,
MAX( h.clock ) AS clock
FROM
zabbix.history h
WHERE
h.itemid in
<foreach collection="itemIds" item="itemid" open="(" separator="," close=")">
#{itemid}
</foreach>
GROUP BY h.itemid
) a on hu.itemid=a.itemid and hu.clock=a.clock
</if>

这张表里没有 51025,51039,51036 指标id,也就是没有 CPU使用率,内存使用率和磁盘使用率这三个指标,自然查不到指标值。

只有 48366,指标值还是 100,不晓得这是个什么指标。

我可以在测试数据库添加几条。

不对,库里有几十万条数据,没显示全。

1
select count(*) from history where itemid between 51008 and 51118;

查过了,这个范围内没有指标值,自己添加指标值吧。

下午两个任务:概览页面结束。

翻页,保留当前监测主机类型

1
2
3
4
5
6
7
8
9
10
11
12
13
def get_config_data(self) -> Dict[str, Any]:
"""统一数据获取入口"""
# 优先调用接口获取数据
api_data = self.fetch_data_from_api(self.monitor_host_type.get("station"))
if api_data is not None:
# 处理返回数据
result = self.handle_api_data(api_data)
# 标记监测类型
self.monitor_type = self.monitor_host_type.get("station")
return result
# 接口失败,直接返回模拟数据
logger.warning("⚠️ 接口调用失败,尝试从数据库获取数据")
return self.generate_mock_data()
1
2
3
4
5
6
7
8
# 当前监测类型
self.monitor_type = 2
# 监测主机类型
self.monitor_host_type = {
"station": 2,
"lane": 3,
"gantry": 4
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
total_records = len(records)
# 计算分页
total_pages = (total_records + self.page_size - 1) // self.page_size # 向上取整
print("总页数:", total_pages)
# 记录总页数
self.total_pages = total_pages

# 如果当前页超过总页数,回到第一页
if self.current_page > self.total_pages > 0:
self.current_page = 1

logger.info(f"当前页码:{self.current_page}/{self.total_pages},总记录数:{total_records}")

# 计算分页索引
start_idx = (self.current_page - 1) * self.page_size
end_idx = min(start_idx + self.page_size, total_records)

# 获取当前页记录
page_records = records[start_idx:end_idx]
logger.info(f"当前页记录:{page_records}, 一共{len(page_records)}条记录")

# 自动翻到下一页,为下次调用做准备
if self.total_pages > 0:
self.current_page += 1
if self.current_page > self.total_pages:
self.current_page = 1

清空控件内容。

1
2
3
4
5
6
7
8
9
10
11
12
# 1. 先清空所有控件的内容
for i in range(self.page_size):
# 清空编号
commands.append(f'{id_controls[i]}.txt=""')
# 清空类型
commands.append(f'{type_controls[i]}.txt=""')
# 清空IP地址
commands.append(f'{ip_controls[i]}.txt=""')
# 清空状态
commands.append(f'{status_controls[i]}.txt=""')
# 重置状态颜色为默认
commands.append(f'{status_controls[i]}.pco=65535') # 白色
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 2. 填充数据
for i, record in enumerate(page_records[:self.page_size], 0):
# 处理状态
status = '在线' if record['is_online'] else '离线'
# 处理类型名称
update_type_name = "站服务器" if record["type_name"] == "收费站服务器" else record["type_name"]
# 编号
commands.append(f'{id_controls[i]}.txt="{i + 1}"')
# 类型
commands.append(f'{type_controls[i]}.txt="{update_type_name}"')
# IP地址
commands.append(f'{ip_controls[i]}.txt="{record["ip"]}"')
# 状态
commands.append(f'{status_controls[i]}.txt="{status}"')
# 根据状态设置颜色
status_color = color_map.get(status, 65535) # 默认白色
commands.append(f'{status_controls[i]}.pco={status_color}')

自增提前。

1
2
3
4
5
# 自动翻到下一页,为下次调用做准备
if self.total_pages > 0:
self.current_page += 1
if self.current_page > self.total_pages:
self.current_page = 1
1
2
# 全局索引
global_index = (self.current_page - 1) * self.page_size + i + 1

饿了,六点多,早点回家去。

这盒子感觉运行几小时就发送信号有问题了,得重启解决,再过几辆车试下看。

2026 年 1 月 20 日

虚拟串口用不了了。

1
2
2026-01-20 12:08:08,041 - INFO - 已跳转到页面 2
2026-01-20 12:08:09,518 - INFO - 收到指令: b'\xb3\xa4\xd6\xce\xb1\xb1\xca\xd5\xb7\xd1\xd5'

接受指令还是不完整。

概览页面接口待完善。

1
2
3
4
5
6
7
def handle_jump_event(self, touch_info):
"""处理跳转事件"""
page_id = touch_info['page']
control_id = touch_info['control']
current_time = time.time()

config_page1_updater = ConfigPage1Updater(self.hmi_connect)
1
2
3
4
5
6
7
8
9
10
class TouchEvent:
def __init__(self, hmi_connect):
self.hmi_connect = hmi_connect
self._serial = hmi_connect.ser
self.data = ""
self.last_touch_time = 0 # 最后触摸时间
self.hex_data = ""
self.if_validate = False
self.dept_id = 0
self.config_page1_updater = ConfigPage1Updater(hmi_connect)

增加读取延迟,确保数据完整。

1
2
3
4
5
6
# 获取原始指令信息
data = _serial.read(_serial.in_waiting)
time.sleep(0.01) # 10ms延迟
# 再读取剩余数据
if _serial.in_waiting > 0:
data += _serial.read(_serial.in_waiting)
1
2
3
4
5
# 获取当前要选择的收费站
current_station = self.stations[self.current_index]
self.current_station = current_station
# 发送选择命令到HMI
self.hmi_connect.send_command(f'b2.txt="{current_station}"')

加载上一次选择。

1
2
3
4
def load_last_station(self):
"""加载上一次选择(收费站)"""
self.hmi_connect.send_command(f'b2.txt="{self.current_station}"')

1
2
# 加载上一次选择
self.load_last_station()

监测资产,翻页。

保留当前查询主机类型(2,3,4),分页获取下一页7条数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
@staticmethod
def _update_config_rows(data: Dict[str, Any]) -> List[str]:
"""更新配置数据行控件(原有逻辑不变)"""
records = data["records"]

commands = []
# 控件ID映射
id_controls = ['t8', 't9', 't10', 't11', 't12', 't13', 't5']
type_controls = ['t14', 't15', 't16', 't17', 't18', 't19', 't6']
ip_controls = ['t20', 't21', 't22', 't23', 't24', 't25', 't7']
status_controls = ['t26', 't27', 't28', 't29', 't30', 't31', 't32']

# 状态颜色映射
color_map = {
"在线": 1696, # 绿色
"异常": 63488, # 红色
"离线": 33840, # 灰色
"警告": 65504, # 黄色
}

# 示例:查看前7条记录
print(f"\n=== 前7条记录示例 ===")
for i, record in enumerate(records[:7], 0):
print(f"{i} | "
f"主机类型: {record['type_name']} | "
f"IP: {record['ip']} | "
f"状态: {'在线' if record['is_online'] else '离线'} | "
f"更新时间: {record['update_time_str']}"
)

# 处理状态
status = '在线' if record['is_online'] else '离线'
# 处理类型名称
update_type_name = "站服务器" if record["type_name"] == "收费站服务器" else record["type_name"]

# 编号
commands.append(f'{id_controls[i]}.txt="{i + 1}"')
# 类型
commands.append(f'{type_controls[i]}.txt="{update_type_name}"')
# IP地址
commands.append(f'{ip_controls[i]}.txt="{record["ip"]}"')
# 状态
commands.append(f'{status_controls[i]}.txt="{status}"')
# 根据状态设置颜色
status_color = color_map.get(status, 65535) # 默认白色
commands.append(f'{status_controls[i]}.pco={status_color}')

return commands

把这个函数修改下,默认是查找前7条数据,现在我要传个参数,用来标记查找1-7,7-14,14-21……这样的数据,直到最后一页没有数据了,那就继续查询前7条数据,其实就是做了个分页,把完整的数据分多次查询展示,通过传参不同,怎么实现?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
page_size = 7
page = 2 # 第二页
start_idx = (page - 1) * page_size # (2-1)*7 = 7
end_idx = start_idx + page_size # 7+7 = 14
records_8_14 = records[start_idx:end_idx] # 第8-14

total_records = len(records)

# 计算分页
total_pages = (total_records + page_size - 1) // page_size # 向上取整
page = max(1, min(page, total_pages if total_pages > 0 else 1)) # 确保页码在有效范围内

# 计算起始和结束索引
start_idx = (page - 1) * page_size
end_idx = min(start_idx + page_size, total_records)

# 如果没有数据,显示第一页
if start_idx >= total_records > 0:
page = 1
start_idx = 0
end_idx = min(page_size, total_records)

切片。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
page_dict = {}
for page_num in range(1, total_pages + 1):
start_idx = (page_num - 1) * page_size
end_idx = min(start_idx + page_size, total_records)
page_records = records[start_idx:end_idx]
page_dict[page_num] = page_records

print(f"\n=== 第{page_num}页 ({start_idx + 1}-{end_idx}/{total_records}) ===")
for i, record in enumerate(page_records, 0):
record_num = start_idx + i + 1
print(f"{record_num} | "
f"主机类型: {record['type_name']} | "
f"IP: {record['ip']} | "
f"状态: {'在线' if record['is_online'] else '离线'} | "
f"更新时间: {record.get('update_time_str', 'N/A')}"
)
print(page_dict[page_num])

翻页。

1
2
3
4
5
6
7
8
9
10
11
12
13
def update_page(self) -> None:
"""翻页"""
all_commands = []
# 更新配置数据行
logger.info(f"---{self.page_dict}")
all_commands.extend(self._update_config_rows(self.page_dict))
# 4. 发送所有命令
success_count = 0
for command in all_commands:
if self.hmi_connect.send_command(command):
success_count += 1
else:
logger.error(f"❌ 命令发送失败: {command}")

2026 年 1 月 19 日

不对,我还得把 Python 程序部署在服务器,比如云控盒上,还要考虑上电重启后程序能正常运行。

t17,t18,改为滚动文本g0,g1。

强制跳转。

1
2
3
4
if target_page == self.current_page:
logger.debug(f"🔄 已在目标页面 {target_page},无需跳转")
print(f"🔄 已在目标页面 {target_page},无需跳转")
return True
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
C:\Users\Lenovo\AppData\Local\Programs\Python\Python314\python.exe -X pycache_prefix=C:\Users\Lenovo\AppData\Local\JetBrains\PyCharmCE2024.1\cpython-cache D:/dev/Pycharm/plugins/python-ce/helpers/pydev/pydevd.py --multiprocess --qt-support=auto --client 127.0.0.1 --port 59310 --file D:\Project\luofeng\touchScreen\code\__init__.py 
Traceback (most recent call last):
File "D:\dev\Pycharm\plugins\python-ce\helpers\pydev\_pydevd_bundle\pydevd_console_integration.py", line 4, in <module>
from code import InteractiveConsole, InteractiveInterpreter
ImportError: cannot import name 'InteractiveConsole' from 'code' (D:\Project\luofeng\touchScreen\code\__init__.py)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "D:\dev\Pycharm\plugins\python-ce\helpers\pydev\pydevd.py", line 49, in <module>
from _pydevd_bundle.pydevd_comm import CMD_SET_BREAK, CMD_SET_NEXT_STATEMENT, CMD_STEP_INTO, CMD_STEP_OVER, \
...<5 lines>...
InternalSendCurrExceptionTraceProceeded, CommunicationRole, run_as_pydevd_daemon_thread
File "D:\dev\Pycharm\plugins\python-ce\helpers\pydev\_pydevd_bundle\pydevd_comm.py", line 91, in <module>
from _pydevd_bundle import pydevd_console_integration
File "D:\dev\Pycharm\plugins\python-ce\helpers\pydev\_pydevd_bundle\pydevd_console_integration.py", line 6, in <module>
from _pydevd_bundle.pydevconsole_code_for_ironpython import IronPythonInteractiveConsole as InteractiveConsole, \
IronPythonInteractiveInterpreter as InteractiveInterpreter
File "D:\dev\Pycharm\plugins\python-ce\helpers\pydev\_pydevd_bundle\pydevconsole_code_for_ironpython.py", line 305
exec code in self.locals
^^^^^^^^^^^^^^^^^^^^^^^^
SyntaxError: Missing parentheses in call to 'exec'. Did you mean exec(...)?

把工作目录改成:D:/Project/luofeng/touchScreen/,而非D:/Project/luofeng/touchScreen/code,没解决报错。

1
2
3
4
5
6
7
8
9
10
11
Traceback (most recent call last):
File "D:\dev\Pycharm\plugins\python-ce\helpers\pydev\_pydevd_bundle\pydevd_pep_669_tracing.py", line 238, in py_start_callback
if not is_thread_alive(thread):
~~~~~~~~~~~~~~~^^^^^^^^
File "D:\dev\Pycharm\plugins\python-ce\helpers\pydev\_pydev_bundle\pydev_is_thread_alive.py", line 18, in is_thread_alive
return t.isAlive()
^^^^^^^^^
AttributeError: 'WriterThread' object has no attribute 'isAlive'. Did you mean: 'is_alive'?
File "D:\dev\Pycharm\plugins\python-ce\helpers\pydev\_pydevd_bundle\pydevd_pep_669_tracing.py", line 238, in py_start_callback
if not is_thread_alive(thread):
~~~~~~~~~~~~~~~^^^^^^^^
1
2
3
Python 3.14202410 月发布的最新版本
PyCharm 2024.120243 月发布的,当时 Python 3.14 尚未发布
JetBrains2024.2 版本中修复了此兼容性问题

监测页。

默认站服务器,点击查询,参数

1
2
3
4
5
6
7
8
9
10
payload = {
"pageSize": 0,
"pageNum": 0,
"orderByColumn": "",
"isAsc": "",
"deptId": 179,
"monitorHostTypeAll": [2,3,4],
"monitorHostType": "2",
"monitorStateType": ""
}

跟我想的有些不太一样。

测试数据库只有长治公司。

今中午不需用睡觉了看来,直接忙活到中午两点多钟了。

1
2
3
4
5
6
# 示例:查看前5条记录
print(f"\n=== 前5条记录示例 ===")
for i, record in enumerate(records[:5], 1):
print(f"{i}. IP: {record['ip']} | 名称: {record['name'] or 'N/A'} | "
f"状态: {'在线' if record['is_online'] else '离线'} | "
f"更新时间: {record['update_time_str']}")
1
2
3
4
5
6
7
8
print(f"\n=== 前8条记录示例 ===")
for i, record in enumerate(records[:8], 1):
print(f"{i} | "
f"IP: {record['ip']} | "
f"状态: {'在线' if record['is_online'] else '离线'} | "
f"更新时间: {record['update_time_str']} | "
f"主机类型: {record['type_name']}"
)

更新汇总信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@staticmethod
def _update_summary(data: Dict[str, Any]) -> List[str]:
"""更新页面底部的汇总信息(原有逻辑不变)"""
summary = data["summary"]
station_hosts = summary['station_hosts'] # 收费站主机数
lane_hosts = summary['lane_hosts'] # 车道主机数
gantry_hosts = summary['gantry_hosts'] # 门架主机数
# 统计各状态数量
commands = [
f't39.txt="{station_hosts}"',
f't40.txt="{lane_hosts}"',
f't48.txt="{gantry_hosts}"',
]
return commands

更新监测数据行控件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
@staticmethod
def _update_monitor_rows(data: Dict[str, Any]) -> List[str]:
"""更新监测数据行控件(原有逻辑不变)"""
records = data["records"]

commands = []
# 控件ID映射
id_controls = ['t8', 't9', 't10', 't11', 't12', 't13', 't5', 't43']
type_controls = ['t14', 't15', 't16', 't17', 't18', 't19', 't6', 't44']
ip_controls = ['t20', 't21', 't22', 't23', 't24', 't25', 't7', 't45']
status_controls = ['t26', 't27', 't28', 't29', 't30', 't31', 't32', 't46']
time_controls = ['g0', 'g1', 'g2', 'g3', 'g4', 'g5', 'g6', 'g7']

# 状态颜色映射
color_map = {
"在线": 1696, # 绿色
"异常": 63488, # 红色
"离线": 33840, # 灰色
"警告": 65504, # 黄色
}

# 示例:查看前8条记录
print(f"\n=== 前8条记录示例 ===")
for i, record in enumerate(records[:8], 0):
print(f"{i} | "
f"主机类型: {record['type_name']} | "
f"IP: {record['ip']} | "
f"状态: {'在线' if record['is_online'] else '离线'} | "
f"更新时间: {record['update_time_str']}"
)
status = '在线' if record['is_online'] else '离线'
# 编号
commands.append(f'{id_controls[i]}.txt="{i}"')
# 类型
commands.append(f'{type_controls[i]}.txt="{record["type_name"]}"')
# IP地址
commands.append(f'{ip_controls[i]}.txt="{record["ip"]}"')
# 状态
commands.append(f'{status_controls[i]}.txt="{status}"')
# 根据状态设置颜色
status_color = color_map.get(status, 65535) # 默认白色
commands.append(f'{status_controls[i]}.pco={status_color}')
# 更新时间
commands.append(f'{time_controls[i]}.txt="{record["update_time_str"]}"')

return commands

页面修改小问题,列表清空,两个页面列表都清空;汇总信息清空,数字左移;IP地址不可编辑;新增点击校验按钮。

第一次查了8条,定时查询第二页第三页……

资产信息后台没有分页查询,那就每过几秒刷新后面的数据,反正都查出来了。

这个目前比较困难,暂时不做。

如何更改控件的前后图层关系 — 淘晶驰串口屏资料中心 1.1.0-2026-01-16 10:28:24 文档

按钮点击后,必然会置顶,妈的。

直接刷新页面。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def query_station_server_info(self) -> bool:
"""查询站服务器信息"""
self.hmi_connect.navigate_to_page(4)
# 获取数据
station_server_info = self.get_station_server_info()
if station_server_info is not None:
# 处理数据
result = self.handle_api_data(station_server_info)
# 更新控件
update_success = self.update_monitor_data(result)
if update_success:
logger.info("✅ 查询站服务器信息成功")
else:
logger.error("❌ 查询站服务器信息失败")
return True

三元表达式。

1
2
# 处理类型名称
update_type_name = "站服务器" if record["type_name"] == "收费站服务器" else record["type_name"]

监听页,配置页,基本完善。

没有下拉列表,点击按钮切换路段公司以及收费站,现在点击校验功能还有老问题。

重复刷新的原因找到了,控件里为了调试方便加了跳转,控件操作直接跳转后代码中没有记录,所以再次跳转仍然可以。

1
2
3
4
5
6
2026-01-19 18:28:20,207 - INFO - 原始指令: b'\xc7\xeb\xca\xe4\xc8\xeb\xd7\xf3\xb2\xe0\xd0\xc5\xcf\xa2\xff\xff\xff'
2026-01-19 18:28:20,207 - INFO - hex指令: C7 EB CA E4 C8 EB D7 F3 B2 E0 D0 C5 CF A2 FF FF FF
2026-01-19 18:28:20,207 - INFO - 数据解析结果: None
2026-01-19 18:28:20,207 - INFO - 点击校验
2026-01-19 18:28:20,207 - INFO - 初始化配置页1
2026-01-19 18:28:20,229 - INFO - 初始化配置页1完毕

原因找到了,进入配置页1后立刻发送了“请输入左边信息”,这个逻辑是后来添加的,原本是要将一段段由 FF FF FF 分割开的字符发送来,解析出输入的信息,但现在解析不了了。

现在只需要输入公司和收费站,IP用来校验完毕后自动填充,继续正确的逻辑。

明天完善优化这个功能,点击校验。

点击下一步,校验右侧的结果信息,有且只有一种可能才能跳转至下一页,这个逻辑我之前写明白了。

暂时不会将两个按钮合并一块。

下班。

2026 年 1 月 16 日

这个问题越深究越发现搞不定。

jasypt 加密,必须要有密码,加密时还会有随机密钥,只要有了加密后的密文,现在还看不到密码,那说明密码被隐藏在别的地方了。

这还得问问密码多少,要不解密不了。

再看看程序吧,不纠结密码问题了。

其实监测,配置1,配置2,首页接口没问题的,外加个下拉框看看怎么适配下,不需要纠结概览页。

概览页也没问题,再梳理下代码看看是不是数据库的问题。

在哪里查出来个阳城南站,晋城的。

1
2
3
101.227.48.102:3306
root
rootaxdQ@1121.1226

路段版本:

1
url: jdbc:mysql://14.103.238.145:10060/sx_rmgt

省中心:

1
jdbc:mysql://101.227.48.102:3306/sx_monitor

2026 年 1 月 13 日

SSH 密钥验证仍然失败,算了先不搞这个。

拉取路段版本代码分支。

查看下数据库账号密码。

1
JasyptEncryptorUtils

application-dev.yaml

1
2
3
username: ENC(K+PL9ntYPPXO8MBMmlWvOA==)
#username: root
password: ENC(C7n/6K8ktMrJ5vmDnzhZM9A7OjAxgesTpfuQoRBQ/k0=)
1
2
3
4
5
jasypt:
encryptor:
password: lybgeek
algorithm: PBEWithMD5AndDES
iv-generator-classname: org.jasypt.iv.NoIvGenerator
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private static final String salt = "lybgeek";

/**
* 解密
*
* @param ciphertext
* @return
*/
public static String decode(String ciphertext) {
System.out.println("加密字符串:" + ciphertext);
ciphertext = "ENC(" + ciphertext + ")";
if (PropertyValueEncryptionUtils.isEncryptedValue(ciphertext)) {
String plaintext = PropertyValueEncryptionUtils.decrypt(ciphertext, basicTextEncryptor);
System.out.println("解密后的字符串:" + plaintext);
return plaintext;
}
System.out.println("解密失败");
return "";
}

执行获取数据库解密后的用户名和密码。

1
2
3
4
5
6
加密字符串:K+PL9ntYPPXO8MBMmlWvOA==
解密后的字符串:root
root
加密字符串:C7n/6K8ktMrJ5vmDnzhZM9A7OjAxgesTpfuQoRBQ/k0=
解密后的字符串:LuoFenMysql@89s#
LuoFenMysql@89s#

路段版本测试环境,数据库连接信息:

1
2
3
14.103.238.145:10060
root
LuoFenMysql@89s#

这样数据库就连接成功了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<select id="selectSectionHostDeptMapByHostTypeAndDeptId" parameterType="java.lang.Byte" resultType="com.tomm.system.domain.SectionHostDeptMap">
SELECT
<include refid="Base_Column_List" />
FROM
t_section_host_dept_map
<where>
<if test="deptIds != null and deptIds.size() > 0">
AND dept_id IN
<foreach item="item" collection="deptIds" open="(" separator="," close=")">
#{item}
</foreach>
</if>
<if test="hostTypes != null and hostTypes.size() > 0">
AND host_type IN
<foreach item="item" collection="hostTypes" open="(" separator="," close=")">
#{item}
</foreach>
</if>
</where>
</select>

启动项目。

1
Error running 'TommApplication' Error running TommApplication. Command line is too long. Shorten the command line via JAR manifest or via a classpath file and rerun.

似曾相识的问题。

调用成功。

1
http://14.103.238.145:10058/dev-api/smallScreenDispla/monitorSummary
1
2
3
4
5
6
7
{
"code": 500,
"msg": "获取资产信息有误",
"data": null,
"success": false,
"error": true
}
1
SynchronizationEventTask

1.路段主机 2.收费站主机 3.车道工控机 4.门架工控机 5门架服务器 6网络设备

现在调用测试服务器老是报获取资产信息有误的错误,调用本机部署的服务有出现 Token 认证错误,首先解决下本机问题,再 Debug。

1
2
3
4
5
6
7
8
9
10
11
12
13
	at com.tomm.framework.config.SaTokenConfig.lambda$null$0(SaTokenConfig.java:49)
at cn.dev33.satoken.router.SaRouterStaff.check(SaRouterStaff.java:213)
at com.tomm.framework.config.SaTokenConfig.lambda$addInterceptors$1(SaTokenConfig.java:47)
at cn.dev33.satoken.interceptor.SaRouteInterceptor.preHandle(SaRouteInterceptor.java:60)
at org.springframework.web.servlet.HandlerExecutionChain.applyPreHandle(HandlerExecutionChain.java:148)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1067)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:965)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
... 68 common frames omitted
2026-01-13 11:32:10 [XNIO-1 task-1] DEBUG c.t.f.i.PlusWebInvokeTimeInterceptor
- <0><18173075801398208> [PLUS]开始请求 => URL[POST /error],参数类型[json],参数:[]
2026-01-13 11:32:10 [XNIO-1 task-1] ERROR c.t.f.w.e.GlobalExceptionHandler
- <0><18173075801398208> 请求地址'/error',认证失败'未能读取到有效Token',无法访问系统资源

数据库查询是没问题的。

1
2
3
4
5
6
SELECT *  -- Base_Column_List 的具体字段
FROM
t_section_host_dept_map
WHERE
dept_id IN ('192')
AND host_type IN ('2', '3')

$2a$10$NHj9.nG3ekSAOQHdAdYS2eWnQZbiQQrKrJ8A5jxP3IbFI4cjwhJnS

JasyptEncryptorUtils

1
2
3
4
5
6
7
8
9
/**
* 认证失败
*/
@ExceptionHandler(NotLoginException.class)
public R<Void> handleNotLoginException(NotLoginException e, HttpServletRequest request) {
String requestURI = request.getRequestURI();
log.error("请求地址'{}',认证失败'{}',无法访问系统资源", requestURI, e.getMessage());
return R.fail(HttpStatus.HTTP_UNAUTHORIZED, "认证失败,无法访问系统资源");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package com.tomm.framework.config.properties;

import cn.hutool.core.util.ReUtil;
import com.tomm.common.annotation.Anonymous;
import com.tomm.common.utils.spring.SpringUtils;
import lombok.Getter;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.util.pattern.PathPattern;

import java.util.*;
import java.util.regex.Pattern;

/**
* 设置注解允许匿名访问的url
*
* @author Lion Li
*/
@Lazy
@Component
public class ExcludeUrlProperties implements InitializingBean {

private static final Pattern PATTERN = Pattern.compile("\\{(.*?)\\}");

@Getter
private final List<String> excludes = new ArrayList<>();

@Override
public void afterPropertiesSet() {
String asterisk = "*";
RequestMappingHandlerMapping mapping = SpringUtils.getBean("requestMappingHandlerMapping", RequestMappingHandlerMapping.class);
Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods();

map.keySet().forEach(info -> {
HandlerMethod handlerMethod = map.get(info);

// 获取方法上边的注解 替代path variable 为 *
Anonymous method = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), Anonymous.class);
Optional.ofNullable(method).ifPresent(anonymous -> {
Set<PathPattern> patterns = info.getPathPatternsCondition().getPatterns();
patterns.forEach(url -> {
excludes.add(ReUtil.replaceAll(url.getPatternString(), PATTERN, asterisk));
});
});

// 获取类上边的注解, 替代path variable 为 *
Anonymous controller = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), Anonymous.class);
Optional.ofNullable(controller).ifPresent(anonymous -> {
Set<PathPattern> patterns = info.getPathPatternsCondition().getPatterns();
patterns.forEach(url -> {
excludes.add(ReUtil.replaceAll(url.getPatternString(), PATTERN, asterisk));
});
});
});
}
}

自动扫描项目中所有被 @Anonymous注解标记的接口,将它们的URL路径收集到 excludes列表中,用于配置不需要认证的接口

如何在GitLab中的个人资料里增加SSH密钥-腾讯云开发者社区-腾讯云

本地启动项目,接口文档地址:OpenAPI definition

1
http://localhost:10057/smallScreenDispla/monitorSummary

通了,注释掉是否有 Token。

1
2
3
4
// 对未排除的路径进行检查
.check(() -> {
// 检查是否登录 是否有token
// StpUtil.checkLogin();

要花时间专门来熟悉下路段版本。

1
2
3
4
5
6
7
//根据部门,查询监测主机的数据
List<SectionHostDeptMap> sectionHostDeptMapList = sectionHostDeptMapService.selectSectionHostDeptMapByHostTypeAndDeptId(
Lists.newArrayList(syncAssetInfoDto.getDeptId()), Lists.newArrayList((byte)10));
log.info("查询概览参数,获取本机资产数据结果:{}", JsonUtils.toJsonString(sectionHostDeptMapList));
if (CollUtil.isEmpty(sectionHostDeptMapList) || sectionHostDeptMapList.size() > RmgtMagicValue.ONE) {
throw new ServiceException("获取资产信息有误");
}

Debug,这里就卡住了。

1
t_section_host_dept_map

见鬼了,为什么这里的 hostTypes 列表默认携带参数为 10。

1
2
3
4
@Override
public List<SectionHostDeptMap> selectSectionHostDeptMapByHostTypeAndDeptId(List<Long> deptIds, List<Byte> hostTypes){
return sectionHostDeptMapMapper.selectSectionHostDeptMapByHostTypeAndDeptId(deptIds, hostTypes);
}

卧艹,这里为什么默认列表第一个值是10.

1
2
3
//根据部门,查询监测主机的数据
List<SectionHostDeptMap> sectionHostDeptMapList = sectionHostDeptMapService.selectSectionHostDeptMapByHostTypeAndDeptId(
Lists.newArrayList(syncAssetInfoDto.getDeptId()), Lists.newArrayList((byte)10));

Lists.newArrayList((byte)10)创建了一个包含单个字节值 10 的 List,表示查询主机类型为 10 的记录。

1
2
3
4
5
6
7
8
XML 中的 parameterType错误
<!-- 错误 -->
parameterType="java.lang.Byte"

<!-- 正确(不写或写为map) -->
<select id="..." resultType="...">
<!-- 或 -->
<select id="..." parameterType="map" resultType="...">
1
2
3
if (CollUtil.isEmpty(sectionHostDeptMapList) || sectionHostDeptMapList.size() > RmgtMagicValue.ONE) {
throw new ServiceException("获取资产信息有误");
}
1
2
3
4
5
6
7
8
/**
* 根据指标id和指标值类型,查询所有类型指标值
* @param itemIds 指标id
* @param type 指标值类型
* @return 指标值集合
*/
List<LatestItemsHistoryVo> selectAllHistoryInfo(@Param("itemIds") List<Long> itemIds, @Param("type") Integer type);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<if test="type=='3'.toString()">
zabbix.history_uint hu
JOIN (
SELECT
h.itemid,
MAX( h.clock ) AS clock
FROM
zabbix.history_uint h
WHERE
h.itemid in
<foreach collection="itemIds" item="itemid" open="(" separator="," close=")">
#{itemid}
</foreach>
GROUP BY h.itemid
) a on hu.itemid=a.itemid and hu.clock=a.clock
</if>

所有 t_section_host_dept_map 表下的部门 id。

1
SELECT DISTINCT dept_id FROM t_section_host_dept_map

返回的接口只有这样的数值,Zabbix 数据库缺失数据,收费站也不应该只有长治的收费站信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"code": 200,
"msg": "操作成功",
"data": {
"ip": "10.14.76.66",
"cpu": "0",
"ram": "0",
"disk": "0",
"uploadBytes": "0",
"downloadBytes": "0",
"memoryTotal": "0",
"memoryUsed": "0.00",
"memoryFree": "0",
"diskTotal": "0",
"diskUsed": "0",
"diskFree": "0.00"
},
"error": false,
"success": true
}

Mysql去重

pycharm控制台输出结果自动换行_pycharm自动换行-CSDN博客

1
2
3
4
# 使用curl测试
curl -X POST http://localhost:10057/smallScreenDispla/monitorSummary \
-H "Content-Type: application/json" \
-d '{"deptId": 192}'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import json
import requests


def monitor_summary_api():
# 定义要发送的JSON数据
payload = {
"deptId": 192,
}

# 设置请求头,指定内容类型为JSON
headers = {
'Content-Type': 'application/json'
}

# 发送POST请求
response = requests.post(
'http://localhost:10057/smallScreenDispla/monitorSummary',
data=json.dumps(payload), # 将字典转换为JSON字符串
headers=headers
)

# 打印响应信息
print(response.status_code)
# print(response.text)

return response.text


if __name__ == '__main__':
monitor_summary_api()

1
2
result = monitor_summary_api()
print(result)

写了个这么几把调用,概览页先这么着吧。

首页。

1
http://localhost:10057/smallScreenDispla/homePageMonitorSummary
1
2
3
{
"deptId": 191
}
1
2
3
4
5
6
7
8
9
10
11
{
"code": 200,
"msg": "操作成功",
"data": {
"monitoredCount": 27,
"abnormalCount": 26,
"updateTime": "2026-01-13 17:37:43"
},
"error": false,
"success": true
}

获取部门下拉。

1
2
3
4
5
@Schema(description = "部门类型,0省中心,1公司,2路段,3收费站")
String ifDeptType;

@Schema(description = "父级id")
Long parentId;
1
2
3
4
5
6
7
8
9
10
11
@Override
public List<SysDept> getSelectedDept(GetSelectedDeptDto getSelectedDeptDto) {
log.info("获取部门下拉入参:{}", JsonUtils.toJsonString(getSelectedDeptDto));
if (ObjUtil.isEmpty(getSelectedDeptDto) || StrUtil.isBlank(getSelectedDeptDto.getIfDeptType())) {
throw new ServiceException("获取部门下拉入参有误,请联系管理员处理!");
}
//调用中心接口,查询部门数据
List<SysDept> sysDepts = mgtClient.selectSysDeptByIfDeptType(getSelectedDeptDto).getData();
log.info("获取部门下拉返回:{}", JsonUtils.toJsonString(sysDepts));
return sysDepts;
}
1
2
3
4
5
6
7
/**
* 根据部门类型查询部门信息
* @param ifDeptType 部门类型
* @return 部门信息
*/
List<SysDept> selectSysDeptByIfDeptType(@Param("ifDeptType") String ifDeptType);

1
2
3
4
5
6
7
<select id="selectSysDeptByIfDeptType" parameterType="java.lang.String" resultType="com.tomm.common.core.domain.entity.SysDept">
SELECT
<include refid="Base_Column_List" />
FROM sys_dept
WHERE (del_flag = '0' and if_dept_type !='0' and if_dept_type=#{ifDeptType}) ORDER BY parent_id ASC,
order_num ASC
</select>
1
2
SELECT * FROM sys_dept
WHERE if_dept_type = 3

0 省中心,1分公司,2路段,3收费站。

查询部门信息,获取部门下拉列表。

1
2
3
4
5
6
7
8
9
10
/**
* 省中心Client
*/
@FeignClient(
name = MgtClient.MGT,
url = "${mgt.url}",
path = "/mgt",
configuration = FeignBypassSSLConfig.class
)
public interface MgtClient {
1
2
3
4
5
6
7
8
/**
* 查询部门信息
*
* @param getSelectedDeptDto 条件
* @return 所有基础数据
*/
@PostMapping(value = "/selectSysDeptByIfDeptType")
R<List<SysDept>>selectSysDeptByIfDeptType(@RequestBody GetSelectedDeptDto getSelectedDeptDto);

从省中心调用接口,获取部门信息。

监听页面。

1
2
3
4
5
@Operation(summary = "监测资产接口,通过条件,查询路段、门架、车道、设备等主机列表")
@PostMapping("/monitorAsset")
public R<MonitorAssetVo> monitorAsset(@RequestBody MonitorAssetDto monitorAssetDto) {
return R.ok(smallScreenDisplayService.monitorAsset(monitorAssetDto));
}
1
2
3
4
5
@Operation(summary = "查询省中心资产信息,通过条件,控制查询公司、路段、收费站层级的数据")
@PostMapping("/selectAssetInfo")
public R<MonitorAssetVo> selectAssetInfo(@RequestBody MonitorAssetDto monitorAssetDto) {
return R.ok(smallScreenDisplayService.selectAssetInfo(monitorAssetDto));
}
1
2
3
4
5
6
7
//查询主机列表
Page<MonitorAssetHost> monitorAssetHostList = sectionHostDeptMapService.selectAssetHostListPage(deptIds,
hostTypes, monitorAssetDto.getMonitorStateType(), monitorAssetDto.build());
//转换HostTypeName
for (MonitorAssetHost monitorAssetHost : monitorAssetHostList.getRecords()) {
monitorAssetHost.setHostTypeName(HostTypeEnum.getValue(monitorAssetHost.getHostType()));
}

查询主机列表,主机列表有记录,但详细记录为空。

1
2
3
4
5
6
7
8
9
/**
* 查询资产主机列表,带监测状态
*
* @param deptIds 部门ID集合(为空时查询所有)
* @param hostTypes 主机类型
* @param page 分页参数
* @return 资产主机状态列表
*/
Page<MonitorAssetHost> selectAssetHostListPage(List<Long> deptIds, List<Byte> hostTypes, String monitorStateType, Page<MonitorAssetHost> page);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<select id="selectAssetHostListPage" parameterType="map" resultType="com.tomm.system.domain.vo.MonitorAssetHost">
SELECT
shdm.host_ip,
shdm.host_type,
shdm.update_time,
h.name as hostName,
if(h.`status`='0','1','0') monitorState
FROM
t_section_host_dept_map shdm
left JOIN
zabbix.hosts h ON h.hostid = shdm.host_id
<where>
<if test="deptIds != null and deptIds.size() > 0">
AND shdm.dept_id IN
<foreach item="item" collection="deptIds" open="(" separator="," close=")">
#{item}
</foreach>
</if>
<if test="hostTypes != null and hostTypes.size() > 0">
AND shdm.host_type IN
<foreach item="item" collection="hostTypes" open="(" separator="," close=")">
#{item}
</foreach>
</if>
<if test="monitorStateType != null and monitorStateType != ''">
AND h.`status`!= #{monitorStateType}
</if>
</where>
</select>
1
2
3
4
FROM
t_section_host_dept_map shdm
left JOIN
zabbix.hosts h ON h.hostid = shdm.host_id

初步判断同样的问题。

zabbix.hosts,监测平台表数据缺失。

2026 年 1 月 12 日

一晃眼一个月时间都快过去了。

这工作安排真够有意思的,看看串口屏开发到了什么地步,接下来先测试 Python 程序调用 Java 接口文档获取接口数据。

小屏显示数据接口,不太完善。

1
2
3
4
5
6
7
8
/**
* 测试接口
*/
@GetMapping("/test")
public String test() {
System.out.println("Hello World");
return "Hello World";
}
1
2
3
4
5
6
7
8
9
def main():
response = requests.get('http://localhost:8080/api/test')
print(response.status_code)
print(response.text)


if __name__ == '__main__':
print(sys.path)
main()

调用接口。

1
2
3
4
5
6
C:\Users\Lenovo\AppData\Local\Programs\Python\Python314\python.exe D:\Project\luofeng\touchScreen\code\test\test.py 
['D:\\Project\\luofeng\\touchScreen\\code\\test', 'D:\\Project\\luofeng\\touchScreen', 'C:\\Users\\Lenovo\\AppData\\Local\\Programs\\Python\\Python314\\python314.zip', 'C:\\Users\\Lenovo\\AppData\\Local\\Programs\\Python\\Python314\\DLLs', 'C:\\Users\\Lenovo\\AppData\\Local\\Programs\\Python\\Python314\\Lib', 'C:\\Users\\Lenovo\\AppData\\Local\\Programs\\Python\\Python314', 'C:\\Users\\Lenovo\\AppData\\Local\\Programs\\Python\\Python314\\Lib\\site-packages']
200
Hello World

Process finished with exit code 0

什么几把接口,每个接口都得我自己实现一遍么,连一个能用的接口都没有。

高速公路联网收费运行监测管理平台

在这里呆着太特么无聊了。

接口得自己写,数据库账号密码都不晓得,写新接口比较费劲,代码都不一定能推的上去,接口写完再调试接口调用赋值。

最后再研究部署,这些活儿都得自己完成,现在连截至时间都没弄明白,下午正好有个周会,有问题能沟通下看能不能解决。

2025 年 12 月 16 日

智能物联终端-蓝湖

一大早就收到了最新的智能物联终端UI最终效果图。

用PS导出纯色色块,所有文字都用 USART HMI 添加文字控件,要不文字分辨率太低。

做完五个页面,怎么一调试就全乱套了,他妈的。

五点四十分,搞定全部五个页面,再次尝试能不能顺利下载,该删的图片都删了。

成功下载,除监测页面获取数据并赋值给对应控件,完成。

明天去郑州,妈的。

2025 年 12 月 15 日

一晃两天过去了,这两天啥也没干。

单片机发送数据给串口屏 — 淘晶驰串口屏资料中心 1.1.0-2025-12-04 09:17:37 文档

改造点击下一步;优化首页、监测页、概览页和配置页的数据获取逻辑,主要整理下返回格式化数据。

1
2
3
4
5
't3.txt="编号"',
't4.txt="类型"',
't5.txt="IP"',
't6.txt="监测状态"',
't7.txt="更新时间"'

上周就发现这个诡异现象了,t7 控件更新失败。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def handle_validate_event(self):
"""处理校验事件"""
logger.info('点击校验')
# 校验本页文本输入
config = ConfigPage1Updater(self.hmi_connect)
# 校验结束
result, commands = config.validate(self.hex_data)
# 校验成功, 标记成功状态
logger.info(f"校验结束, 标记结果状态:{result}; 校验结果信息: {commands}")
logger.info("=" * 50)
self.if_validate = result
# 显示校验结果
for command in commands:
self.hmi_connect.send_command(command)
1
2
3
4
5
6
7
8
9
class ConfigPage1Updater:
"""简化版配置页面处理"""

def __init__(self, hmi_connect):
self.hmi_connect = hmi_connect
self.current_page = 2
self.next_page = 3
self.hmi_connect.send_command(f't7.pco=1055')
self.hmi_connect.send_command(f't7.txt="请输入信息"')

问题可能出在这了。

问题出个几把,删了这段都没反应。

点击下一步。

点击下一步,跳转业务逻辑全部封装点击校验,校验成功就显示校验成功信息,显示再点击一次下一步就成功跳转配置页2;校验失败就显示校验错误信息,跟之前的点击校验逻辑是一样的。

把点击校验的功能封装到点击下一步,

【腾讯文档】加班统计表
https://docs.qq.com/sheet/DTEFLY3p2R0tQV0pU?tab=000001

新增 api_pages 页面,初步完善从后台接口文档获取真实数据的功能。

问题不在于 config_page_2 或者 monitor_page 页面编码,不需要在这里排查了。

1
2
3
4
logger.info("初始化配置页1")
self.hmi_connect.send_command(f't7.pco=1055')
self.hmi_connect.send_command(f't7.txt="请输入信息"')
logger.info("初始化配置页1完毕")
1
2
3
4
5
6
7
8
9
10
11
2025-12-15 10:59:05,839 - INFO - 点击下一步前,校验结果:True
2025-12-15 10:59:05,839 - INFO - 已跳转到页面 3
2025-12-15 10:59:05,839 - INFO - 📊 获取到 9 条监测数据
2025-12-15 10:59:05,913 - INFO - 📊 监测页面更新完成: 71/71 条命令成功 (100.0%)
2025-12-15 10:59:05,913 - INFO - ✅ 监测页面更新成功
2025-12-15 10:59:06,398 - INFO - 点击校验
2025-12-15 10:59:06,398 - INFO - 初始化配置页1
2025-12-15 10:59:06,398 - INFO - 初始化配置页1完毕
2025-12-15 10:59:06,398 - INFO - 🔍 校验输入: IP=$, 公司=, 收费站=
2025-12-15 10:59:06,398 - INFO - 校验结束, 标记结果状态:False; 校验结果信息: ['t7.txt="校验未通过\r\n路段公司不\r\n能为空"', 't7.pco=63488']
2025-12-15 10:59:06,398 - INFO - ==================================================

这特么什么原因。

点击下一步,怎么接下来还监听到校验事件了。

1
2
3
4
5
6
7
2025-12-15 11:05:25,433 - INFO - ✅ 监测页面更新成功
2025-12-15 11:05:25,916 - INFO - 校验前检查下受到了什么数据:b'$\xff\xff\xff'
2025-12-15 11:05:25,916 - INFO - 点击校验
2025-12-15 11:05:25,916 - INFO - 初始化配置页1
2025-12-15 11:05:25,917 - INFO - 初始化配置页1完毕
2025-12-15 11:05:25,917 - INFO - 🔍 校验输入: IP=$, 公司=, 收费站=
2025-12-15 11:05:25,917 - INFO - 校验结束, 标记结果状态:False; 校验结果信息: ['t7.txt="校验未通过\r\n路段公司不\r\n能为空"', 't7.pco=63488']

24 FF FF FF 串口缓冲区溢出 — 淘晶驰串口屏资料中心 1.1.0-2025-12-12 10:12:30 文档

找到原因了。

1
2
3
4
5
2025-12-15 11:20:00,349 - INFO - 📊 监测页面更新完成: 72/72 条命令成功 (100.0%)
2025-12-15 11:20:00,349 - INFO - ✅ 监测页面更新成功
2025-12-15 11:20:00,892 - INFO - 数据解析结果: None
2025-12-15 11:20:00,892 - INFO - 校验前检查下受到了什么数据:b'$\xff\xff\xff'
2025-12-15 11:20:00,892 - INFO - 校验前检查下受到了什么数据:24 FF FF FF

当串口缓冲区被占满以后会返回此数据

缓冲区溢出以后,缓冲队列里的指令执行完成后会为缓冲区腾出空间以继续接收指令,在此之前,串口收到的数据将会丢弃

常见原因:

  1. 发送的速度太快了,建议每条指令间隔100毫秒
  2. 发送了太多无关的指令,串口屏无法进行解析,导致串口缓冲区被占满。
1
2
3
4
5
# 添加结束符并发送
full_command = command.encode('gbk') + b'\xFF\xFF\xFF'
self.ser.write(full_command)
# 休眠100毫秒
time.sleep(0.1)

设置指令发送间隔 100 毫秒。

1
2
3
2025-12-15 11:25:27,796 - INFO - 数据解析结果: None
2025-12-15 11:25:27,796 - INFO - 校验前检查下受到了什么数据:b'\x1a\xff\xff\xff\x1a\xff\xff\xff\x1a\xff\xff\xff\x1a\xff\xff\xff\x1a\xff\xff\xff\x1a\xff\xff\xff\x1a\xff\xff\xff\x1a\xff\xff\xff\x1a\xff\xff\xff\x1a\xff\xff\xff'
2025-12-15 11:25:27,796 - INFO - 校验前检查下受到了什么数据:1A FF FF FF 1A FF FF FF 1A FF FF FF 1A FF FF FF 1A FF FF FF 1A FF FF FF 1A FF FF FF 1A FF FF FF 1A FF FF FF 1A FF FF FF

又出现了新的问题。

1
2
3
4
5
total_commands = len(all_commands)
success_rate = (success_count / total_commands) * 100 if total_commands > 0 else 0

logger.info(f"📊 监测页面更新完成: {success_count}/{total_commands} 条命令成功 ({success_rate:.1f}%)")
return success_count == total_commands
1
2
3
4
try:
if self.ser and self.ser.is_open:
# 清空缓冲区
self.clear_buffer()
1
2
3
4
5
6
7
8
9
# 发送所有命令
success_count = 0
for command in all_commands:
if self.hmi_connect.send_command(command):
success_count += 1
else:
logger.error(f"❌ 命令发送失败: {command}")
# 清空缓冲区
self.hmi_connect.ser.read_all()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 发送串口指令
def send_command(self, command: str) -> bool:
"""发送指令到串口屏"""
try:
if self.ser and self.ser.is_open:
# 清空缓冲区
self.clear_buffer()
# 添加结束符并发送
full_command = command.encode('gbk') + b'\xFF\xFF\xFF'
self.ser.write(full_command)
# 清空缓冲区
self.clear_buffer()
# 休眠100毫秒
time.sleep(0.01)
return True

这清空缓冲区都写烂了,串口屏还是会收到 1A FF FF FF。

1
2
3
4
5
6
7
8
9
10
def update_monitor_data(self, data: List[Dict[str, Any]]):
"""更新监测页面所有控件数据"""
try:
all_commands = []
# 1. 更新标题和汇总信息
all_commands.extend(self._update_header_controls())
# 2. 更新监测数据行
# all_commands.extend(self._update_monitor_rows(data))
# # 3. 更新汇总统计
# all_commands.extend(self._update_summary(data))

找到问题了,问题出在更新监测数据行和更新汇总统计后。

1
2
# 统计信息文本(如果有对应控件的话)
# f't_summary.txt="正常率: {normal_rate:.1f}% 异常: {status_count.get("异常", 0)}个"',

他妈的这里有个更新统计信息文本。。。

1
2
3
4
5
# 如果状态异常,添加闪烁效果
if status == "异常":
commands.append(f'{status_controls[i]}.blink=1')
else:
commands.append(f'{status_controls[i]}.blink=0')

他妈的这里还有个状态异常添加闪烁效果。。。

问题全部排除。

1
2
# 休眠100毫秒
time.sleep(0.01)

各条命令之间休眠 100 毫秒是很有必要的。

1
prints "QUERY",10 
1
2
3
4
5
6
7
# 查询事件
if data == b'query':

return {
'type': constants.Constants.QUERY_EVENT,
'data': touch_info,
}

your-backend-api.com

他妈的这么个随便生成的 API 接口地址,竟然是真实存在的,怪不得调用还真花了点时间。

1
2
3
4
5
6
# 新增:HTTP接口配置(可根据实际情况修改)
API_CONFIG = {
"url": "http://your-backend-api.com/system/overview", # 后台接口地址
"timeout": 5, # 接口超时时间
"method": "GET" # 请求方法:GET/POST
}

云里雾里优化到下午四点多钟,完善各个页面的请求接口文档获取真实数据功能,他妈的接口文档还不知道呢,等明天慧敏来了再问问。

Docker 部署 Python 服务。

1
2
3
# 循环监听
update_count = 0
HomePageUpdater(self.hmi_connect).run()

他妈的这怎么又行了。

1
2
3
4
5
6
7
8
9
10
11
def get_monitor_data(self) -> List[Dict[str, Any]]:
"""统一数据获取入口"""
# 优先调用接口获取数据
api_data = self.fetch_data_from_api()
if api_data is not None:
# 处理接口返回数据
result = self.handle_api_data(api_data)
return result
# 接口失败,直接返回模拟数据
logger.warning("⚠️ 接口调用失败")
return self.generate_mock_data()
1
2
3
4
def handle_api_data(self, api_data: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
"""处理接口返回数据"""
data = []
return data

先把三个查询接口写完整了,再想想怎么处理分页查询。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 查询站服务器信息(监测页)
elif page_id == 1 and control_id == 6:
logger.info('点击查询站服务器信息')
monitor = MonitorPageUpdater(self.hmi_connect)
monitor.query_station_server_info()
# 查询车道信息(监测页)
elif page_id == 1 and control_id == 7:
logger.info('点击查询车道信息')
monitor = MonitorPageUpdater(self.hmi_connect)
monitor.query_lane_info()
# 查询门架信息(监测页)
elif page_id == 1 and control_id == 8:
logger.info('点击查询门架信息')
monitor = MonitorPageUpdater(self.hmi_connect)
monitor.query_door_info()
1
2
3
4
5
6
7
def query_station_server_info(self) -> bool:
"""查询站服务器信息"""
# 获取数据
data = self.get_station_server_info()
# 更新控件
self.update_monitor_data(data)
return True
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@staticmethod
def get_station_server_info() -> List[Dict[str, Any]] | None:
"""查询站服务器信息"""
try:
logger.info(f"📡 正在调用查询站服务器信息接口: {API_CONFIG['url']}")
# 根据请求方法发送请求
if API_CONFIG["method"].upper() == "GET":
response = requests.get(API_CONFIG["url"], timeout=API_CONFIG["timeout"])
else:
# 若为POST,可根据需要添加data/json参数(示例:传递空json)
response = requests.post(
API_CONFIG["url"],
json={}, # 可根据接口要求修改请求体(如传递分页参数)
timeout=API_CONFIG["timeout"]
)
# 检查响应状态码(非200则抛出异常)
response.raise_for_status()
# 解析接口返回的JSON数据
api_data = response.json()
logger.info("✅ 站服务器信息获取成功")
return api_data

except requests.exceptions.RequestException as e:
# 捕获所有requests相关异常(超时、连接失败、状态码错误等)
logger.error(f"❌ 站服务器信息获取失败: {e}")
return None
except ValueError as e:
# 捕获JSON解析失败异常
logger.error(f"❌ 站服务器信息数据解析失败: {e}")
return None

查询功能基本完善,跟页面查询一样,就剩对接真实的后台接口文档以及处理并返回真实数据了,接下来搞搞分页查询。

分页思路不好操作。

不像前端框架那样有很方便的分页插件啥的。

算了,分页不做了,监测页面获取数据后定时查询,初步想法是每隔三秒查询下一页数据,循环查询。

2025 年 12 月 12 日

1
咱们屏幕上的软件,是有java后台的,你只要用python调用http api就可以了,不用重新操作数据库

Python调用java接口_python 使用json调取java接口-CSDN博客

调试结束。

Memory/touch-screen

1
git@gitee.com:deng-2022/touch-screen.git

项目换个目录,他妈的怎么就导入包不成功了。

1
2
from code.utils.touch_event import TouchEvent
from code.utils.hmi_connect import HMIConnect

(亲测解决)PyCharm 从目录下导包提示 unresolved reference(完整图解)_unresolved reference怎么解决-CSDN博客

他妈的解决了,新增 code 目录必须要勾选 new Python Package 选项,而不能勾选 new Directory 选项。

点击下一步,先点击校验,校验成功的信息是固定的,判断校验信息是否为"校验通过\r\n点击下一步",则跳转,反之显示需校验通过

进入配置页,前初始化事件发送校验结果文本。

1
2
3
4
5
6
7
8
if(t7.txt!="")
{
prints t7.txt,30 //发送ip地址
printh ff ff ff //发送帧尾
}else
{
printh ff ff ff //发送空数据
}

touch-screen: 基于上位机软件 USART HMI 开发的 淘晶驰 T1 系列 4.3 寸 tft 液晶显示屏(串口屏)

1
2
3
4
5
6
7
8
9
def handle_validate_event(self):
"""处理非触摸事件"""
logger.info('点击校验')
# 校验本页文本输入
config = ConfigPage1Updater(self.hmi_connect)
commands = config.validate(self.hex_data)
print(f"结果:{commands}")
for command in commands:
self.hmi_connect.send_command(command)

这里的 self.hex_data 是不合理的,进入配置页抓取校验文本信息,抓取方法不应该放在全局监听下,应该独立写一个方法。

重新捋逻辑。

任何操作都会被监听,处理监听事件目前分为处理触摸事件(这个触摸是指同简单的页面更新绑定,因为触摸意味着跳转页面)、处理文本校验事件,后续完善处理跳转事件、处理其他事件。

把文本校验放在全局监听中,获取校验文本值,保存至 Config1updater 的 self 值中,在处理文本校验事件以及其他时间中,直接引用。

设计是合理的。

1
2
3
4
5
6
def __init__(self, hmi_connect):
self.hmi_connect = hmi_connect
self.current_page = 2
self.next_page = 3
self.hmi_connect.send_command(f't7.pco=1055')
self.hmi_connect.send_command(f't7.txt="请输入信息"')

妈的,默认数据直接在 USART HMI 中默认指定不就行了,哪里还用用得着解析前初始化事件发来的文本数据。

接下来处理点击下一步逻辑。

1
2
3
4
5
6
7
8
if(t7.txt!="")
{
prints t7.txt,30 //发送ip地址
printh ff ff ff //发送帧尾
}else
{
printh ff ff ff //发送空数据
}

前初始化事件也不应该发送文本,发送了之后他妈的就监听到文本校验事件了。

后续需要考虑下跳转事件,校验事件以及查询事件的区别。

为什么,用Python程序实现跳转页面,只有第一次有效,之后就无效了,都走了同样的函数逻辑为什么第二次点击按钮后,串口屏页面就不跳转了。

1
🔄 已在目标页面 3,无需跳转

他妈的,日志输出害我不浅,这条 DEBUG 日志就没打印出来。

1
2
3
4
5
6
# 返回至首页
elif (page_id == 1 and control_id == 3 or
page_id == 2 and control_id == 3 or
page_id == 3 and control_id == 3 or
page_id == 4 and control_id == 3):
logger.info('返回至主页')

点击返回,返回上一页。

点击配置2页面的上一页后,用 Python 程序实现跳转,保留 current_page 属性为 2,再次跳转页面 3 就没问题。

点击配置1页面的上一页后,用 Python 程序实现跳转,保留 current_page 属性为 0,……

不对,不能重复跳转是逻辑问题,理论上应该用 Python 程序实现所有页面跳转。

1
2
3
4
5
# 跳转至概览页
if page_id == 0 and control_id == 3:
logger.info('跳转至概览页')
overview = OverviewPageUpdater(hmi_connect)
overview.run()
1
2
3
elif page_id == 4 and control_id == 3:
logger.info('概览页返回至主页')
self.hmi_connect.navigate_to_page(0)

要重新写个启动类了,影响 home_page 的导入。

1
2
3
4
from code.pages.monitor_page import MonitorPageUpdater
from code.pages.overview_page import OverviewPageUpdater
from code.pages.config_page_1 import ConfigPage1Updater
from code.pages.config_page_2 import ConfigPage2Updater
1
2
3
4
if __name__ == '__main__':
# 使用示例
init = InitControl(HMIConnect(port='COM5', baudrate=115200))
init.main()

新增启动类,直接负责启动系统,但首页首次渲染有问题,不应该有问题啊,暂时解决返回至首页的页面刷新问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 返回至首页
elif page_id == 1 and control_id == 3:
logger.info('监测页返回至首页')
HomePageUpdater(self.hmi_connect).run()

elif page_id == 2 and control_id == 3:
logger.info('配置页1返回至首页')
HomePageUpdater(self.hmi_connect).run()

elif page_id == 3 and control_id == 3:
logger.info('配置页2返回至主页')
HomePageUpdater(self.hmi_connect).run()

elif page_id == 4 and control_id == 3:
logger.info('概览页返回至主页')
HomePageUpdater(self.hmi_connect).run()

点击下一步。

1
2
3
4
5
6
7
8
9
//page config_2     //跳转至下一页
if(t7.txt!="")
{
prints t7.txt,30 //发送校验结果
printh ff ff ff //发送帧尾
}else
{
printh ff ff ff //发送空数据
}

不行,太混乱了,直接选择在点击校验完毕后保存校验结果,保存到服务端,点击下一步不用紊乱调用处理校验事件的函数了。

1
2
3
4
5
# 校验通过,返回校验正确
logger.info("✅ 输入校验通过")
self.validate_success = f't7.txt="校验通过\r\n点击下一步"'
commands.append(self.validate_success)
commands.append(f't7.pco=1696')

那这个方法就算白写了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@staticmethod
def get_validate_result_value(hex_data) -> dict:
"""解析校验结果输入值"""
# 按分隔符拆分
separator = 'FF FF FF'
hex_parts = hex_data.split(separator)
# 删掉最后一个空串
hex_parts = hex_parts[:-1]
# 去掉每段两端的空格
cleaned_parts = [p.strip() for p in hex_parts]
# 创建结果字典
result = {}
# 根据位置分配字段
if len(cleaned_parts) > 0:
result['validate'] = cleaned_parts[0]
else:
result['validate'] = ""

return result

校验结束,校验成功后标记,

1
2
3
4
5
# 标记校验成功
self.if_validate = True
print(f"标记校验成功,{self.if_validate}")
for command in commands:
self.hmi_connect.send_command(command)

点击下一步前,检查校验结果。

1
2
3
4
5
6
7
8
9
elif page_id == 2 and control_id == 9:
logger.info('点击下一步')
# 判断校验结果, 校验成功,跳转至配置页2
print(f"点击下一步前,校验结果: {self.if_validate}")
if self.if_validate:
ConfigPage2Updater(self.hmi_connect).run()
# 校验失败,提示用户
else:
logger.info('请先完成配置')

启用监听程序,把对象实例化放在循环监听外,解决问题,正常运行逻辑。

1
2
3
4
5
# 启用监听程序
touch_event = TouchEvent(self.hmi_connect)
while True:
try:
touch_event.display_touch_listener()

正常个几把,重新优化代码,校验结果返回元组,校验结果标记。

1
2
3
4
5
6
7
8
9
# 校验结束
result, commands = config.validate(self.hex_data)
# 校验成功, 标记成功状态
logger.info(f"校验结束, 标记结果状态:{result}; 校验结果信息: {commands}")
logger.info("=" * 50)
self.if_validate = result
# 显示校验结果
for command in commands:
self.hmi_connect.send_command(command)

慢着。

点击下一步。如果校验结果成功,那就跳转,如果不成功呢,校验结果为任何未通过校验的信息,默认无法跳转,此时保留校验结果;那如果是初始默认的请输入信息呢。

去他妈的,只要校验没通过,就固定提醒没有校验通过,改个黄色的字体。

点击下一步基本完成,但还是有点不太方便。

整合成一个按钮,就点击下一步。

点击下一步,先校验字段,如果未通过校验,那就显示失败原因

2025 年 12 月 11 日

单片机发送数据给串口屏 — 淘晶驰串口屏资料中心 1.1.0-2025-12-04 09:17:37 文档

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
if(t1.txt!="")
{
prints t1.txt,30 //发送ip地址
printh ff ff ff //发送帧尾
}else
{
printh ff ff ff //发送空数据
}
if(t6.txt!="")
{
prints t6.txt,30 //发送路段公司
printh ff ff ff //发送帧尾
}else
{
printh ff ff ff //发送空数据
}
if(t5.txt!="")
{
prints t5.txt,30 //发送收费站
printh ff ff ff //发送帧尾
}else
{
printh ff ff ff //发送帧尾
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@staticmethod
def get_input_value(hex_data) -> dict:
print(f"原始数据: {hex_data}")
# 按分隔符拆分
separator = 'FF FF FF'
hex_parts = hex_data.split(separator)
print(f"去掉分隔符: {hex_parts}")
# 删掉最后一个空串
hex_parts = hex_parts[:-1]
print(f"删掉最后一个空串: {hex_parts}")
# 去掉每段两端的空格
cleaned_parts = [p.strip() for p in hex_parts]
print(f"去掉两端空格: {cleaned_parts}")
# 创建结果字典
result = {}
# 根据位置分配字段
if len(cleaned_parts) > 0:
result['ip'] = cleaned_parts[0]
else:
result['ip'] = ""

if len(cleaned_parts) > 1:
result['company'] = cleaned_parts[1]
else:
result['company'] = ""

if len(cleaned_parts) > 2:
result['station'] = cleaned_parts[2]
else:
result['station'] = ""

return result
1
2
3
4
5
6
7
8
9
10
11
# 解析输入值, 不能为空
input_value = self.get_input_value(hex_data)
if not input_value:
logger.error("输入不能为空")
return "输入为空"

# 解析IP地址, 公司,收费站
ip = self.reverse_hex_to_text(input_value["ip"], encoding='utf-8') # IP地址
company = self.reverse_hex_to_text(input_value["company"], encoding='gbk') # 路段公司
station = self.reverse_hex_to_text(input_value["station"], encoding='gbk') # 收费站
logger.info(f"🔍 校验输入: IP={ip}, 公司={company}, 收费站={station}")
1
2
3
4
5
原始数据: D0 A3 D1 E9 CE B4 CD A8 B9 FD 0D 0A 49 50 B2 BB C4 DC CE AA BF D5 FF FF FF
去掉分隔符: ['D0 A3 D1 E9 CE B4 CD A8 B9 FD 0D 0A 49 50 B2 BB C4 DC CE AA BF D5 ', '']
删掉最后一个空串: ['D0 A3 D1 E9 CE B4 CD A8 B9 FD 0D 0A 49 50 B2 BB C4 DC CE AA BF D5 ']
去掉两端空格: ['D0 A3 D1 E9 CE B4 CD A8 B9 FD 0D 0A 49 50 B2 BB C4 DC CE AA BF D5']
结果:['t7.txt="校验未通过\r\n路段公司不\r\n能为空"', 't7.pco=63488']

点击校验,核心功能完善结束。

下一步。

点击下一步,获取提示信息状态,结果符合预期则正常跳转,反之点击无效吧,不用搞什么花里胡哨的了。

1
2
# 处理跳转事件
# 处理其他事件

2025 年 12 月 10 日

单片机发送数据给串口屏 — 淘晶驰串口屏资料中心 1.1.0-2025-12-04 09:17:37 文档

1
prints t1.txt,30

将一个变量或常量从串口发送出去。

1
printh ff ff ff      //发送帧尾

每条数据发送出去后,添加帧尾结束符,Python程序读取比较方便。

1
2
3
4
5
6
7
8
9
10
prints t1.txt,30
printh ff ff ff //发送帧尾

prints t6.txt,30
printh ff ff ff //发送帧尾

prints t5.txt,30
printh ff ff ff //发送帧尾

page config_2 //跳转至下一页

配置页,点击校验,监听,解析数据,校验数据(校验不通过,返回错误信息;校验通过,返回校验成功信息),下一步,跳转页面。

1
2
3
4
5
6
def test(hex_data):
print("测试")
# 转换为字节
hex_str = hex_data.replace(' ', '')
data_bytes = bytes.fromhex(hex_str)
print(f"-----{data_bytes}-----")

最后测试一遍接受串口屏发来的数据。

1
2
3
4
hex_str = hex_data.replace(' ', '')

# 以 FF FF FF 分割数据
hex_parts = hex_data.split(' FF FF FF ')
1
-----['65 02 09 00', '31 39 32 2E 31 36 38 2E 32 2E 37 35', 'D4 CB B3 C7 B9 AB CB BE', 'D4 CB B3 C7 CA D5 B7 D1 D5 BE FF FF FF']-----
1
2
3
4
5
6
7
8
9
10
11
12
13
@staticmethod
def get_input_value(hex_data) -> str:
"""获取输入框的值(这里用模拟值)"""
# 实际项目中需要从HMI读取控件值
hex_str = hex_data.replace(' ', '')
# 收到第2条数据: 65 02 09 00 FF FF FF 31 39 32 2E 31 36 38 2E 32 2E 37 35 FF FF FF D4 CB B3 C7 B9 AB CB BE FF FF FF D4 CB B3 C7 CA D5 B7 D1 D5 BE FF FF FF
# 以 FF FF FF 分割数据,删除末尾的 FF FF FF
hex_parts = hex_data.split(' FF FF FF ')
# hex_parts = hex_parts[:-1]
print(f"-----{hex_parts}-----")
for part in hex_parts:
print(f"数据含义: {part}")
return ""

他妈的发送键值,发送的是触发按钮事件吗,把点击校验触发指令默认不接受,要不然点击后收不到接下来的配置页面输入文本内容了。

取消勾选点击校验->发送键值。

1
2
3
4
5
6
7
8
9
elif page_id == 2 and control_id == 11:
logger.info('点击校验')
# 获取原始数据(十六进制格式)
hex_data = self.get_serial_data()
print(f"原始数据: {hex_data}")
config = ConfigPage1Updater(hmi_connect)
# 校验本页文本输入
result = config.validate(self.hex_data)
print(f"结果: {'成功' if result else '失败'}")

取消发送键值的话,这个监听就判断不到点击校验按钮有没有被按到了,逻辑在这里没有通过。

需要考虑下两种方案,接受发送键值能否获取到输入内容;不接受发送键值又该如何让逻辑走通。

1
2
3
4
5
6
7
8
else:
# 如果不是标准触摸事件,尝试显示文本内容
try:
text = data.decode('utf-8', errors='ignore').strip()
if text:
logger.info(f"文本内容: {text}")
except Exception as e:
logger.info(f"无法解析数据格式: {e}")

这里改一下,不是标准触摸事件,在这个串口屏下就只有一种可能,那就是校验文本输入。

返回个特殊值,返回 None 还不行。

返回一个对象,再根据返回数据类型判断是不是标准触摸事件,校验文本输入归为其他事件,正常调用。

1
2
3
4
return {
'type': 'touch_event',
'data': touch_info,
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
def display_touch_listener(self):
"""监听触摸事件并处理页面跳转"""
# logger.info("触摸事件监听程序已启动,监听COM5端口,等待触摸事件...")
# logger.info("=" * 50)
try:
# 监听触摸事件
result = self.listen_for_touch_event()

# 无事件,持续监听中
if result is None:
return

event_type = result["type"]
data = result["data"]

# 处理触摸事件
if event_type == "touch_event":
self.handle_touch_event(self.hmi_connect, data)

# 处理其他事件
if event_type == "raw_data":
self.handle_other_event(self.hmi_connect, data)

except Exception as e:
logger.error(f"❌ 触摸监听出错: {e}")
return
1
2
3
4
5
6
7
8
def handle_other_event(self, hmi_connect, data):
"""处理非触摸事件"""
logger.info('点击校验')

# 校验本页文本输入
config = ConfigPage1Updater(hmi_connect)
result = config.validate(self.hex_data)
print(f"结果: {'成功' if result else '失败'}")

2025 年 12 月 9 日

重新优化一遍触摸监听模块。

控件发送的信号只有:页面id,控件id,按下/弹起,所有信号包括页面跳转都应该由Python脚本发送,而不应该绑定到控件上。

我研究下控件信号还有什么名堂,发送键值是什么意思。

他妈的,这就是将来Python脚本要做的,控制页面跳转页面文本控件赋值

页面跳转实现:硬编码,监听指定页面id 下的按钮控件id,根据业务逻辑硬编码跳转至指定页面;

文本控件赋值:

淘晶驰资料中心 — 淘晶驰串口屏资料中心 1.1.0-2025-12-04 09:17:37 文档

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 发送串口指令
def send_command(self, command: str) -> bool:
"""发送指令到串口屏"""
try:
if self.ser and self.ser.is_open:
# 添加结束符并发送
full_command = command.encode('gbk') + b'\xFF\xFF\xFF'
self.ser.write(full_command)
logger.debug(f"发送指令: {command}")
time.sleep(0.1) # 短暂延迟确保指令处理
return True
else:
logger.error("串口未连接")
return False
except Exception as e:
logger.error(f"发送指令失败: {e}")
return False
1
这样的Python发送指令程序,默认都是在所有控件刷新之后才执行,我要实现这样的指令在跳转页面后所有控件刷新之前执行,怎么实现,怎么改造Python代码

AI 没法交流。

1
command = f'{control_id}.txt="{escaped_text}"'

文本控件赋值,只能尝试以下两种方案里的一种:

  1. 跳转页面之前,跳转页面之前执行所有控件赋值,这个得看下上面的赋值命令能不能改,没有尝试过指定页面id下的控件id赋值的。
  2. 照旧,页面控件默认值全部清空,刷新慢就刷新慢吧。

怪了,为什么刷新慢呢。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 跳转至指定页面
def navigate_to_page(self, target_page: int) -> bool:
"""跳转到指定页面"""
try:
if target_page == self.current_page:
logger.debug(f"🔄 已在目标页面 {target_page},无需跳转")
return True

command = f"page {target_page}"
success = self.send_command(command)
if success:
self.current_page = target_page
logger.info(f"已跳转到页面 {target_page}")
time.sleep(1) # 等待页面加载
return success

他妈的谁写的代码,就是这里跳转页面之后,还休眠了一秒钟才继续执行的,,。正常执行控件内容赋值很快的。

还是得清空页面控件默认值,以前是用来做测试数据的,相当于用假数据填充完毕基本实现原型图来着。

1
页面0,控件3,7,8
1
首页 ——> 启动监听 ——> 监听串口屏触摸事件(页面id | 控件id | 事件) ——> 执行指令(跳转页面 | 返回数据)
1
CPU35% 内存90%(容量32G|已用25G|剩余17G) 磁盘70%(容量32G|已用25G|剩余17G) 网络流量(上行1M/s|下行1M/s)
1
2
3
4
# 跳转至主页面(页面0)
if not self.hmi_connect.navigate_to_page(0):
logger.error("无法跳转到主页面")
return

神经了,这代码,跳转所有页面都可以,就是不能跳转页面0。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
2025-12-09 16:44:36,911 - INFO - 跳转至概览页
2025-12-09 16:44:36,911 - INFO - 📊 生成模拟数据完成
2025-12-09 16:44:36,918 - INFO - ✅ 概览页面更新完成: 26/26 命令成功 (100.0%)
2025-12-09 16:44:36,924 - INFO - 🕒[16:44:36] 收到数据: 1A FF FF FF 1A FF FF FF 1A FF FF FF
2025-12-09 16:44:36,924 - INFO - 文本内容: 
2025-12-09 16:44:36,934 - INFO - 🕒[16:44:36] 收到数据: 1A FF FF FF
2025-12-09 16:44:36,934 - INFO - 文本内容: 
2025-12-09 16:44:36,936 - INFO - 🕒[16:44:36] 收到数据: 1A FF FF FF
2025-12-09 16:44:36,936 - INFO - 文本内容: 
2025-12-09 16:44:37,001 - INFO - 🕒[16:44:37] 收到数据: 1A FF FF FF
2025-12-09 16:44:37,001 - INFO - 文本内容: 
2025-12-09 16:44:37,002 - INFO - 🕒[16:44:37] 收到数据: 1A FF FF FF
2025-12-09 16:44:37,002 - INFO - 文本内容: 
2025-12-09 16:44:37,004 - INFO - 🕒[16:44:37] 收到数据: 1A FF FF FF 1A FF FF FF 1A FF FF FF 1A FF FF FF 1A FF FF FF

搞不清楚为什么会出现这种问题。

用Python程序控制页面跳转,妈的返回首页后就无效了,跳转不成功但获取数据成功了。

不消耗时间了,暂时用控件执行跳转,Python只负责获取并返回数据。

1
2
3
4
# 跳转至主页面(页面0)
if not self.hmi_connect.navigate_to_page("index"):
logger.error("无法跳转到主页面")
return

改成这样。

1
2
3
4
5
6
7
8
9
10
# 使用示例
if __name__ == "__main__":
# 创建配置
config = MySQLConfig(
host="localhost",
port=3306,
database="test",
username="root",
password="Dw990831"
)

随便写个 MySQL 数据库配置测试类,连接数据库没问题。

1
2
3
4
5
6
7
8
9
10
# 使用示例
if __name__ == "__main__":
# 创建管理器
manager = MySQLManager(MySQLConfig())
# 执行查询
if manager.connect():
# 查询
user_info = manager.execute_query("SELECT * FROM user")
print(f"用户信息: {user_info}%")
manager.close()
1
pip install pymysql
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def execute_query(self, query: str, params: tuple = None) -> Optional[List[Dict]]:
"""执行查询并返回结果"""
# 数据库未连接且执行连接失败
if not self.connection:
if not self.connect():
return None

try:
# 执行查询
with self.connection.cursor(pymysql.cursors.DictCursor) as cursor:
cursor.execute(query, params or ())
result = cursor.fetchall()
return result
except Exception as e:
logger.error(f"❌ 查询执行失败: {e}")
return None
1
2
3
# 查询
user_info = manager.execute_query("SELECT * FROM user where age = 20")
print(f"用户信息: {user_info}%")

执行数据库查询,结果如下。

1
2
3
4
C:\Users\Lenovo\AppData\Local\Programs\Python\Python314\python.exe D:\Project\python\HMI\touchScreen\config\mysql_config.py 
2025-12-09 18:03:05,662 - INFO - ✅ 已连接到MySQL数据库: localhost:3306/test
2025-12-09 18:03:05,664 - INFO - 🔌 MySQL连接已关闭
用户信息: [{'id': 3, 'name': '邓嘻嘻', 'age': 20, 'sex': '男', 'job': '运维工程师', 'current_page': None, 'page_size': None}]%

2025 年 12 月 5 日

今天务必把所有必须的 Python 程序全部编写运行并测试完毕。

罢了,今中午得去一趟调试顶棚灯,这程序谁爱写谁写去,看有时间再完善下吧,目前都是模拟数据没有从指定数据库中获取。

2025 年 12 月 4 日

他妈的刚发现电脑的USB转TTL模块怎么不亮了,这样接笔记本的串口屏也跟着亮不起来了对吧。

昨天下半前不整挺好的么,我拿着鼠标和转录模块的USB口调换一下,妈的现在连鼠标都动不了了,电脑USB口给这个插口搞坏掉了吧。

艹。

折腾了快一个小时,这个模块上有个黑色的滑块按钮,分别是用来开关USB转TTL模块电源的,开了就没事了。

用手机充电器也行,还是买个专用的 12V2A 电源适配器,四十块钱左右。

1
2025/12/04 10:25:47

encode 选择 gbk 和 gb2312,这两个字体竟然有细微差别吗,选择 gbk 编码的汉字字体至少在模拟器和串口屏上显示更清晰点。

用手机充电器也行,还是买个专用的 12V2A 电源适配器,四十块钱左右。

十一点半,基本完善全部五个页面,接下来就是Python脚本了,今早听他们沟通结果应该是要调整串口屏尺寸大小,完事了还得写什么测试文档开发文档。

明天去车道干啥。。

2025 年 12 月 3 日

新增字库,指定字符,要不这字库文件加起来就有10MB,下载到串口屏都报错了。

1
File is too large for destination device.

报错:file is too large for destination device — 淘晶驰串口屏资料中心 1.1.0-2025-11-21 14:36:46 文档

数据库还没有拿到,先编写除了数据库连接以外的 Python 监听程序吧。

python出现PEP 8: E302 expected 2 blank lines, 问题和IndentationError:unexpected indent问题_pep 8: e302 expected 2 blank lines, found 1-CSDN博客

基本完善了首页指令收发逻辑。

启动监听。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 使用示例
def main():
# 创建数据填充器实例
hmi_filler = HMIDataFiller(port='COM5', baudrate=115200)

try:
# 连接串口屏
if not hmi_filler.connect():
return

# 跳转至主页面(页面0)
if not hmi_filler.navigate_to_page(0):
logger.error("无法跳转到主页面")
return

# 单次更新所有控件数据
logger.info("开始更新页面数据...")
success = hmi_filler.update_all_controls()

if success:
logger.info("数据更新完成")

# 询问是否启动定时更新
user_input = input("是否启动定时更新?(y/n): ")
if user_input.lower() == 'y':
interval = int(input("请输入更新间隔(秒): ") or "30")
hmi_filler.start_periodic_update(interval)
else:
logger.error("数据更新失败")

except Exception as e:
logger.error(f"程序执行出错: {e}")
finally:
hmi_filler.close()

今下午把所有页面基本写完,模拟数据也搞上,还可以进一步尝试下把 Python 程序给部署到服务器上,还没试过。


1
我有个问题还想问,就是现在本机编写的Python程序能够监听到本机串口屏,能发送信号到串口屏也能接受来自串口屏的信号,那么将来呢,将来在生产环境下,是不是得在连接串口屏的那台主机上把这套Python代码运行起来呢,思路是这样的吧。

docker 部署python项目(实战)_docker部署python项目-CSDN博客

1
CPU35% 内存90%(容量32G|已用25G|剩余17G) 磁盘70%(容量32G|已用25G|剩余17G) 网络流量(上行1M/s|下行1M/s)
1
2
3
4
5
6
7
# 第一对:t25(服务名)和 t24(状态)
commands.append(f't8.txt="{services[0]["name"]}"')
status_text1 = f'{services[0]["status"]}'
status_color1 = color_map.get(services[0]["status"])
# 设置状态文本和颜色
commands.append(f't25.txt="{status_text1}"')
commands.append(f't25.pco={status_color1}')

2025 年 12 月 2 日

1
写了些测试页面,笔记本用转接模块连接串口屏,本地Python程序能监听到串口屏控件发来的触发信号,Python程序也能发送指令操作串口屏
1
你找找产品,我们有个触摸屏的软件,原来是普通web项目,要改成串口屏

串口屏开发应该选择什么编码?

编码格式 适用场景 优点 缺点 推荐指数
GB2312 简体中文环境 兼容性好,体积小 不支持繁体,字符集有限 ⭐⭐⭐⭐
GBK 中文字符扩展 支持更多汉字,兼容GB2312 非国际标准 ⭐⭐⭐⭐⭐
UTF-8 国际化项目 全球通用,支持所有语言 体积稍大,部分老设备兼容性差 ⭐⭐⭐⭐
ASCII 纯英文环境 体积最小,兼容性最好 不支持中文 ⭐⭐
Big5 繁体中文环境 台湾香港地区标准 不支持简体中文 ⭐⭐
Shift-JIS 日文环境 日本地区专用 仅限日文项目
1
智能物联监测终端背景

包图网-正版商用视频音乐素材库,AI设计图片|PPT模板|海报|背景元素,会员免费下载

千图网-免费在线设计图片素材网站-正版商用素材图库模板大全

ps快速修改尺寸和分辨率 超级简单版

妈的这个尺寸下的字体大小,最小字体高度也得是16,屏幕尺寸不大这字体显得就臃肿了,先按照这样的大小设置吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 发送串口指令
def send_command(self, command: str) -> bool:
"""发送指令到串口屏"""
try:
if self.ser and self.ser.is_open:
# 添加结束符并发送
full_command = command.encode('gbk') + b'\xFF\xFF\xFF'
self.ser.write(full_command)
return True
else:
logger.error("串口未连接")
return False
except Exception as e:
logger.error(f"发送指令失败: {e}")
return False
1
2
3
4
5
2025-12-10 17:19:22,379 - ERROR - 💥 处理失败: list index out of range
2025-12-10 17:19:22,383 - INFO - 🕒[17:19:22] 收到数据: 1A FF FF FF
2025-12-10 17:19:22,383 - INFO - 点击校验
2025-12-10 17:19:22,383 - ERROR - 💥 处理失败: list index out of range
2025-12-10 17:19:22,386 - INFO - 🕒[17:19:22] 收到数据: 1A FF FF FF

又出现串口屏发来的特殊指令了。

1
2025-12-10 17:24:57,736 - INFO - 🕒[17:24:57] 收到数据: 1A FF FF FF

校验结束返回校验结果,串口品那个自动相应信息,就是这么个信息被监听程序归为其他触摸指令,反复监听解析没有通过。

解决两个问题:

为什么变量无效呢?

1
return f"t7.txt=校验通过\n点击下一步"
1
return f't7.txt="校验通过\r\n点击下一步"'

这样也能正常换行,这里的编码暂时硬编码吧:

1
2
3
4
commands.append(f't7.txt="校验通过\r\n点击下一步"')
commands.append(f't7.pco=1696')

return commands
1
2
3
4
5
6
# 校验本页文本输入
config = ConfigPage1Updater(self.hmi_connect)
commands = config.validate(self.hex_data)
print(f"结果:{commands}")
for command in commands:
self.hmi_connect.send_command(command)

那出现了特殊指令又该怎么解析呢?暂时不处理,要处理也是在这里添加代码:

1
2
3
4
5
6
7
# 处理触摸事件
if event_type == "touch_event":
self.handle_touch_event(self.hmi_connect, data)

# 处理其他事件
if event_type == "raw_data":
self.handle_other_event()

1C FF FF FF 赋值操作失败 — 淘晶驰串口屏资料中心 1.1.0-2025-12-10 10:22:10 文档

1A FF FF FF 变量名称无效 — 淘晶驰串口屏资料中心 1.1.0-2025-12-10 10:22:10 文档

当ip为空时,报错未按预期,是无效指令,其实三个文本输入竟然都不能为空,为空串口屏就发来无效指令。

00 FF FF FF 无效指令 — 淘晶驰串口屏资料中心 1.1.0-2025-12-10 10:22:10 文档

1
32 2E 32 2E 38 2E 38 FF FF FF FF FF FF D4 CB B3 C7 CA D5 B7 D1 D5 BE

路段公司为空时,两个 FF FF FF 挨在一块儿了。

1
2
3
4
5
prints t1.txt,30
printh ff ff ff //发送帧尾
prints t6.txt,30
printh ff ff ff //发送帧尾
prints t5.txt,30
1
2
3
4
5
6
7
8
9
10
11
12
if(t1.txt!="")
{
prints t1.txt,30 //发送ip地址
printh ff ff ff //发送帧尾
}else if(t6.txt!="")
{
prints t6.txt,30 //发送路段公司
printh ff ff ff //发送帧尾
}else if(t1.txt!="")
{
prints t5.txt,30 //发送收费站
}

这样发送之前判空,填写为空就不发送了,看下发送的数据格式指令是

1
2
3
4
from HMI.touchScreen.pages.monitor_page import MonitorPageUpdater
from HMI.touchScreen.pages.overview_page import OverviewPageUpdater
from HMI.touchScreen.pages.config_page_1 import ConfigPage1Updater
from HMI.touchScreen.pages.config_page_2 import ConfigPage2Updater

更新代码后,全部输入,出现报错:

1
输入: ['31 39 32 2E 31 36 38 2E 32 2E 37 35 FF FF FF']
1
2
3
4
5
6
7
8
9
10
11
12
13
14
if(t1.txt!="")
{
prints t1.txt,30 //发送ip地址
printh ff ff ff //发送帧尾
}
if(t6.txt!="")
{
prints t6.txt,30 //发送路段公司
printh ff ff ff //发送帧尾
}
if(t1.txt!="")
{
prints t5.txt,30 //发送收费站
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 一次性发送所有非空字段
prints t1.txt,30
if(t1.txt!="")
{
printh ff ff ff
}

prints t6.txt,30
if(t6.txt!="")
{
printh ff ff ff
}

prints t5.txt,30
if(t5.txt!="")
{
printh ff ff ff
}

最终版本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
if(t1.txt!="")
{
prints t1.txt,30 //发送ip地址
printh ff ff ff //发送帧尾
}else
{
prints "",30 //发送空数据
printh ff ff ff //发送帧尾
}

if(t6.txt!="")
{
prints t6.txt,30 //发送路段公司
printh ff ff ff //发送帧尾
}else
{
prints "",30 //发送空数据
printh ff ff ff //发送帧尾
}

if(t5.txt!="")
{
prints t5.txt,30 //发送收费站
}else
{
prints "",30 //发送空数据
printh ff ff ff //发送帧尾
}

不对。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
if(t1.txt!="")
{
prints t1.txt,30 //发送ip地址
printh ff ff ff //发送帧尾
}else
{
printh ff ff ff //发送空数据
}

if(t6.txt!="")
{
prints t6.txt,30 //发送路段公司
printh ff ff ff //发送帧尾
}else
{
printh ff ff ff //发送空数据
}

if(t5.txt!="")
{
prints t5.txt,30 //发送收费站
printh ff ff ff //发送帧尾
}else
{
printh ff ff ff //发送帧尾
}

这里的编码从一开始就错了,我为了从原始数据中获取十六进制数据中更省事,最后一条数据没有增加结束帧标志,导致现在解析数据混乱。

无论是否为空,都要有明显的结束帧,接下来解析数据的逻辑又要重新编写,着重解析 FF FF FF 的位置,用其位置索引定能将数组分割成三部分,无论文本控件输入的内容是否为空。

这样就规范了。

Debian

2025 年 12 月 2 日

从网络安装 Debian

Index of /cdimage/archive/10.13.0/amd64

加速器

2025 年 12 月 2 日

1
其实加速软件原理很简单,就是利用VPN技术,用户通过一台登陆服务器用加速软件商提供的账号密码拨号登陆到一台具有双线带宽的服务器上(专业术语叫:节点服务器)并与之建立连接并改变当前(即本机的上网环境)网络环境。在访问的时候,将本机访问的目标(例如:一个网址)通过节点服务器转发一次,从而完成加速效果。

城乡医保

2025 年 12 月 1 日

“职工医保”VS“居民医保”,六不同!看完就懂→

职工医保VS居民医保,有啥区别?交哪个好

城乡医保交一年保一年,不交也行,不交也不能强制交,花钱给他们完成任务了,只是不交有人找麻烦。

就跟五六十年代人民公社一样,说的入社退社自由,不入就有人批斗你。

2026 年 1 月 12 日

城乡居民医保根本就不用交了,今年年初已经给交了职工医保。

GitCode

2025 年 12 月 1 日

不一样的 Git 之间 | GitLab vs GitHub vs Gitee vs GitCode_gitcode和github区别-CSDN博客

(99+ 封私信 / 82 条消息) 码云gitee的Pages服务下线了,有那些替代品? - 知乎

AtomGit | GitCode - 全球开发者的开源社区,开源代码托管平台

我的工作台 - AtomGit | GitCode

1
你既然知道现在是多会儿,所以我问你截至目前为止 AtomGit 还提供 Pages 服务吗,最近它可是经历过一次大更新,跟 GitCode 这玩意儿合并了,而据我所知新版本的 AtomGit 暂时不提供 Pages 服务,请你谨言慎行给我准确的答复。

搜索 - 腾讯云开发者社区-腾讯云

成为技术影响力大牛? CODING Pages 快速搭建个人专属博客-腾讯云开发者社区-腾讯云

CNB - Cloud Native Build

1
2
3
令牌名:blog
Git Username:cnb
Token:eD0SFwiyUxawqdM3d84iPJvZXiD

云端快速初始化,一键迁移远程仓库。

物流

2025 年 11 月 26 日

快递公司分类及关联

Harbor

2025 年 11 月 21 日

基于Docker、JenKins、Nginx、Harbor、Github及腾讯云服务器搭建个人博客基于Docker、Je - 掘金

私有镜像仓库 Harbor 的安装与配置在云原生时代,各种系统服务都以 Docker 容器的方式运行着。镜像仓库,顾名思 - 掘金

Docker私服-Habor由于掘金平台的摘要有强制字数要求,这里需要给一下默认文字作为摘要。这里是掘金平台的默认摘要, - 掘金

使用 Docker、Jenkins、Harbor 和 GitLab 构建 CI/CD 流水线 随着 DevOps 文化 - 掘金

设计流程

  1. 使用 Gitlab 作为代码仓库
  2. 使用 Harbor 作为镜像仓库
  3. 使用 Jenkins 发布系统部署
  4. Docker 管理 Jenkins + Harbor + Gitlab

工作流程

  1. 迁入代码 push 到 Gitlab
  2. Gitlab 触发 webhook 的 push 触发事件并主动通知 Jenkins 构建
  3. Jenkins 在 Gitlab 获取源码并通过配置好的规则与 shell 脚本进行构建

如果是工具库,则 dotnet push 到私有 Nuget;

如果是 Web 应用,则通过 dockerfile 构建 docker 镜像并 push 到 Docker Registry,然后由 docker swarm create 多节点

交换机

2025 年 11 月 20 日

什么是交换机

IP 盒子

2026 年 1 月 15 日

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
root@loongson-pc:/opt/deviceagent# cat config/config.yaml
- outCommunicator:
type: GBOutCommunicator
options:
serverIp: "192.168.2.34"
serverPort: 9547
laneHex: "1401240121"
certificationInfo: "Trawe"
deviceAgents:
"feeInfoBoard_01,alarm_01,trafficLight_01":
type: feeInfoBoard
options:
type: ShanXiStdFeeInfoBoard
portName: /dev/ttyS4
"barrierMachine_01":
type: barrierMachine
options:
type: ShanXiXinTongIODevice
#type: IODeviceFake
inputIoNumber: -1
openIOBit: "0"
closeIOBit: "1"
needSimulatingInput: "False"
ipAddress: 192.168.2.75
port: 502
"vehicleDetector_01":
type: vehicleDetector
options:
#type: IODeviceFake
type: ShanXiXinTongIODevice
inputIoNumber: 6
needSimulatingInput: "False"
ipAddress: 192.168.2.75
port: 502
"vehicleDetector_02":
type: vehicleDetector
options:
#type: IODeviceFake
type: ShanXiXinTongIODevice
inputIoNumber: 7
needSimulatingInput: "False"
ipAddress: 192.168.2.75
port: 502
"vehicleDetector_03":
type: vehicleDetector
options:
#type: IODeviceFake
type: ShanXiXinTongIODevice
inputIoNumber: 5
needSimulatingInput: "False"
ipAddress: 192.168.2.75
port: 502
"vehicleDetector_04":
type: vehicleDetector
options:
#type: IODeviceFake
type: ShanXiXinTongIODevice
inputIoNumber: 4
needSimulatingInput: "False"
ipAddress: 192.168.2.75
port: 502
"plateRecognizer_01":
type: plateRecognizer
options:
#type: PlateRecognizerFake
type: ShanXiStdPlateRecognizer
libraryPath: "./devlib/camera/zhongweiob/lpr.so"
mediaUploadUrl: http://192.168.2.34:8080/pic/picImage

2026 年 1 月 13 日

1
2
3
4
5
6
7
@木雨 联调完了,正式环境测试要求提供:
1.国标后台服务端ip和端口
2.lanehex
3.唯一标识码
4.牌识图片http上传的url

1. 3. 4. 帮忙提供下吧
1
2
3
192.168.2.34:9528
Trawe
http://192.168.2.34:8080/axle/axlelmage
1
2
3
4
1.国标后台服务端ip和端口,192.168.2.34:9528
2.lanehex,1401DD0126
3.唯一标识码,Trawe
4.牌识图片http上传的url,http://192.168.2.34:8080/axle/axlelmage

明天中午正式环境测试,特微的也会在现场协助。

2026 年 1 月 12 日

1
今天看能不能跟孙工确认一下正式环境的测试,以及开道试用@回忆如初

2026 年 1 月 8 日

1
2
测试工具发的顶棚屏的控制命令能收到, 对接没问题;
不过, 控制顶棚牌的串口发了后没反应, 我再看看发送的命令, 或找厂家看看.
1
2
3
4
5
那今天先这样吧, 顶棚牌,车检器跟特微的对接没问题;
顶棚牌的串口协议可能要另外时间调一下;
牌识的抓拍命令跟特微对接没问题, 但出车牌可能要车辆;

小黄人的, 先拿ip, 然后进网页看看怎么配置国标协议的服务端和端口, 然后也需要车来测试.
1
2
3
4
费显联调正常;IO输入接口正常;折叠屏,牌识跟特微的对接没问题;
折叠屏的串口协议需要另外时间再调,目前向串口发命令后没反应,得检查下命令或者问问厂家;
牌识的抓拍指令正常,用牌识纸不好抓车牌,后续调试最好过车抓拍测试;
联调IO,IO的控制命令跟国标不一致,待解决

多问两句,这小黄人还有折叠屏就不用调了。

1
小黄人先不用调,是旁道有小黄人,我昨天看错了
1
现场折叠屏串口还没接,现在是用桌子上的小键盘控制的,后续应该会转成网口,这个暂时不用调了
1
接了以前顶棚灯的串口,跟现在折叠屏是不通的。

调试进度。

1
2
3
栏杆: 国标控制命令问题
牌识: 没车识别不到车牌,需确认图片http接口
小黄人: 没条件测试
1
折叠屏:没条件测试,且后续要转网口
1
车检器,费显:已调通

2026 年 1 月 6 日

[linux vi全选并复制粘贴 - 腾讯云开发者社区 - 腾讯云](https://cloud.tencent.com/developer/information/linux vi全选并复制粘贴)

Linux Vim撤销和恢复撤销快捷键-腾讯云开发者社区-腾讯云

1
cd /opt/scaner/lib/config
1
cat Config.ini
1
vim Config.ini

image-20260106134714886

1401DD0114

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
[LaneId]
laneId=1401DD0126

[Log]
ManageLogMode=0
DelJudgeHour=2

[Factory]
Enable=1
WSServerPort=9902

[Env]
Test=0

[HttpCfg]
PicImageUrl=http://127.0.0.1:8080/pic/picImage

[Holiday]
;入口节假日客一是否需要云坐席确认,默认否:0
holidaymode=1
;入口节假日前后发纸券时间(小时),默认:4
holidaytimer=6

;出口节假日客一是否需要云坐席确认,默认否:0
NeedAuth=0
;出口节假日提前操作纸券时间(小时),默认:4
PaperTicketHours=6

[LaneType]
NoBody=1
;默认 2:出口机器人车道,3:入口ETC专道/Web车道B节点部署,4:出口ETC专道,5:MTC开放式道,1:入口机器人车道
LaneType=2

[CardSettings]
;上工位卡盒告警阈值
CardAlarmThres1=10
;下左工位卡盒告警阈值
CardAlarmThres2=10
;下右工位卡盒告警阈值
CardAlarmThres3=10

[Device]
;出口卡数满报警阈值
MaxCardNum=200

[Dip]
ip=https://10.14.210.189:3004/

[Ws]
;云坐席
#url=ws://10.14.210.188:6660/robotWbs/robot?laneId=
#url=ws://10.14.210.188:16660/robotWbs/robot?laneId=

;云监控
#monitorurl=ws://10.14.210.188:6660/charges-lane-monitor/monitor/lane?laneId=
monitorurl=ws://10.14.210.188:16660/charges-lane-monitor/monitor/lane?laneId=

BakMode=1

[IcReader]
;读卡器数量,默认:2
ReaderCnt=2

;读卡器1加载的动态库路径,入出口机器人默认:/opt/scaner/lib/libICC_GV.so,ETC专道和开放式车道默认:/opt/scaner/lib/libReaderApi.so
libPath1=/opt/scaner/lib/libReaderApi_kezhuan.so
;读卡器1串口,入出口机器人默认:/dev/ttysWK23,ETC专道和开放式车道默认:/dev/ttyS1
com1=/dev/ttysWK23

;读卡器2加载的动态库路径,入出口机器人默认:/opt/scaner/lib/libICC_GV.so,ETC专道和开放式车道默认:/opt/scaner/lib/libReaderApi.so
libPath2=/opt/scaner/lib/libReaderApi_kezhuan.so
;读卡器2串口,入出口机器人默认:/dev/ttysWK30,ETC专道和开放式车道默认:/dev/ttyS8
com2=/dev/ttysWK30

;读卡器3(若有)加载的动态库路径,出口机器人默认:/opt/scaner/lib/libICC_GV.so
libPath3=/opt/scaner/lib/libReaderApi_kezhuan.so
;读卡器3(若有)串口,出口机器人默认:/dev/ttysWK31
com3=/dev/ttysWK31

Reader3=/dev/ttysWK31
;上工位读卡器序号,默认:1
IndexReader1=1
;上工位读卡器插Psam卡的卡槽号,默认2
PSAMSockID1=1
;下工位读卡器序号,默认:2
IndexReader2=2
;下工位读卡器插Psam卡的卡槽号,默认2
PSAMSockID2=1

[IssuanceMode]
;默认0:插卡机器人 1:抛卡机器人
throwcardmode=0

;发卡机类型 0-无人值守 1-嵌入式 2-大容量 3-第三方机器人 4-标准化自助发卡机器人模式
issuancemode=4

[Mode]
;默认0-插卡机器人 1-抛卡机器人
RobotMode=0

[MobilePay]
EnableAutoCancelOder=0
EnableShowDetail=1

[CashPay]
;现金支付界面显示详细信息(开票二维码),默认0:不显示,1:显示
EnableShowDetail=1

[CustomOption]
;启动机器人时内屏界面是否显示等待启动界面,默认1:显示 0:不显示
WaitInit=0

[GUI]
;配置快速输入的车牌列表,默认为空:软件自动分配(逻辑为本省+周边省+汽车保有量较多省份);配置不为空的话,则按照配置的分配,如:粤|鲁|苏|浙|豫
FastInputPlate=

[Screen]
;控制内屏的屏幕索引,默认:0,(配置为0是1024*768分辨率,1是1440*900分辨率)
InnerScreenIndex=0
;控制外屏的屏幕索引,默认:1,(配置为0是1024*768分辨率,1是1440*900分辨率)
ExScreenIndex=1

[Log]
;需清理的其它日志文件路径,有多个用"|"分割,默认为空
;OtherLogClearPath=D:/Code/tianjinNew/project/build-CommonFrame2.0_Windows-Desktop_Qt_5_7_0_MinGW_32bit-Release/scaner/lib/log/./
;需清理的其它日志文件保存天数,有多个用"|"分割,默认:60
;OtherLogSaveDays=60
;需清理的其它日志文件后缀,默认:.log
;OtherLogFilters=*.zip

[Server]
;服务监听的端口数量(此配置与ListenPortx匹配),默认:1
ListenCnt = 2
;服务监听的端口1,默认:默认9547
ListenPort1 = 9547
;服务监听的端口2,默认:默认9548
ListenPort2 = 8085

[SysParams]
;stdDevice使用的设备类型,默认2:入口机器人设备,1:B节点设备,3:出口机器人设备,4:ETC专道/开放式车道设备,5:自定义支持的设备(根据DevList来配置)
DevType=5

;当DevType配置为5时启用此配置,默认为空,多个设备则用|分割,目前自定义只支持:F0:非接触IC卡读写器、F2:支付受理终端、EC:称重检测设备、F8:关道机、E9:情报板、F9:行车诱导灯,若要支持支付受理终端、称重检测设备,则配置为:F2|EC
DevList=E2|E6|E9|EA

;stdBusiness进程服务部署的IP地址,默认:127.0.0.1
BusiServIp=192.168.131.135
;stdBusiness进程服务监听的端口,默认:9547
BusiServPort=9547

;车道设备验证信息,默认:Trawe
CertInfo=Trawe

[ModuleSwitch]
;使能读卡器代理
CardReader=1
HardwareApp=1

[LaneInfo]
;车道设备验证信息,默认:Trawe
CertInfo=Trawe

;外场服务端地址,默认为空
;OutFieldUrl=ws://localhost:9209/edge-modules-control/ctrl/ws?appId=
HardwareControlUrl=ws://10.0.23.75:9091
OutFieldUrl=ws://10.0.23.75:9209
;OutFieldUrl=ws://10.0.23.75:9209
[Coil]
;针对标准化协议地感序号,与当前特微地感序号差异,故有此转换定义
;地感设备编号1,转发给外场的地感设备编号配置,默认:1
CoilIndex1=4
;地感设备编号2,转发给外场的地感设备编号配置,默认:2
CoilIndex2=2
CoilIndex3=3

[TopBoard]
;默认0:旧的设备使用方式,1:C-Power7-A折叠屏
DevType=2
IP=172.32.204.13
;IP=127.0.0.1
Port=5200
;上班情报板显示信息
LoginShowText=ETC/自助|山西高速欢迎您
#LoginShowText=节假日免费|山西高速欢迎您
;下班情报板显示信息
LogoutShowText=车道关闭|山西高速欢迎您

[FeeBoard]
;针对标准化协议费显序号,与当前特微外场控制费显序号差异,故有此转换定义
;外场控制费显设备编号(外场默认1:前费县,2:后费显)1,转发给标准化费显设备的编号配置,默认:1
FeeBoardIndex1=0
;外场控制费显设备编号(外场默认1:前费县,2:后费显)2,转发给标准化费显设备的编号配置,默认:2
FeeBoardIndex2=1

;执行费显清屏指令,1:执行,0:不执行,默认:1
EnableClearFeeBoardCtrl=0
DevType=1

[LaneClose]
;默认0:特微关道机,1:爱特思关道机
DevType=1
IP=172.32.204.14
Port=2929

[Funtion]
;识别车型结果缓存时间,默认30min
CacheRegVehTypeOverTimeMin=30

[DriveCarLight]
com=/dev/ttysWK31
BaudRate=9600

[Weight]
;默认0:旧的设备使用方式,1:山西富甲园区站地磅
DevType=2
com=/dev/ttyUSB3
BaudRate=9600
Library=./lib/devlib/weightctrl/libWtDevDll.so
ProtocolType=0

[MobilePay]
;移动支付设备类型,默认0:特微移动支付,1:山西华软移动支付
DevType=1
;当DevType配置为1时,移动支付后台地址
PayUrl=http://10.14.3.227:8031/pay.htm
;当DevType配置为1时,终端唯一识别码
DevNo=ADAAAACYG
;当DevType配置为1时,车道编号
LaneNo=54
;当DevType配置为1时,站编号
ExStationNo=1422101


[Printer]
;上工位打印机索引,1表示使用打印机1打印,默认:1
UpPrinterIndex=1
;下工位打印机索引,2表示使用打印机2打印,默认:2
DownPrinterIndex=2


[StdDev]
;是否提前上传岛尾节点抓拍(不带图片内容)结果给外场,默认1:提前上传 0:不提前上传
PreUploadTailCaptureResult=1

2026 年 1 月 5 日

盒子问题解决了,改成自动配置再重新手动配置下,要不紊乱了。

1
2
3
4
5
6
7
8
9
以太网适配器 以太网 3:

连接特定的 DNS 后缀 . . . . . . . :
本地链接 IPv6 地址. . . . . . . . : fe80::e92a:234:e120:5608%20
IPv4 地址 . . . . . . . . . . . . : 192.168.1.228
子网掩码 . . . . . . . . . . . . : 255.255.255.0
IPv4 地址 . . . . . . . . . . . . : 192.168.2.11
子网掩码 . . . . . . . . . . . . : 255.255.255.255
默认网关. . . . . . . . . . . . . : 192.168.2.1

2025 年 12 月 12 日

1
@SZH 折叠屏的部标服务端,要配置为ip盒子的ip和端口,端口可以配9901
1
但折叠屏ip都不知道,更不用谈设置方法了

2025 年 12 月 5 日

1
sudo su -
1
sudo chown loongson:loongson /home/loongson/package/
1
sudo chown root:root /home/loongson/package/
1
dpkg -i ./i386-runtime-base_1.2.1~20230714.lnd.1_loongarch64.deb
1
tar -zxvf lat-new-c4055e9.tar.gz
1
export LD LIBRARY_PATH=`pwd`:$LD_LIBRARY_PATH

2025 年 11 月 27 日

1
sudo systemctl status ssh

**如果看到 Unit ssh.service not found**:说明系统未安装SSH服务器。需要安装:

1
2
sudo apt update
sudo apt install openssh-server
1
uname -m

https://mirrors.aliyun.com/ubuntu-ports/pool/main/o/openssh/

ubuntu-ports-pool-main-o-openssh安装包下载_开源镜像站-阿里云

Index of /pool/main/o/openssh

银河麒麟桌面操作系统V10(SP1)离线升级SSH(OpenSSH)服务_麒麟系统离线安装ssh-CSDN博客

1
2
sudo apt update
sudo apt install net-tools
1
ssh loongson@192.168.1.112

网工必懂!路由器、光猫、交换机三大设备大揭秘,区别你分得清吗?-腾讯云开发者社区-腾讯云

分不清路由器、交换机、光猫的概念,一文带你迅速搞懂!

光猫路由器傻傻分不清?看完这篇稳如老狗

分不清路由器、交换机、光猫的概念,一文带你迅速搞懂!-阿里云开发者社区

2025 年 11 月 26 日

麒麟服务器操作系统–网络配置的五种方式_麒麟系统配置网卡ip地址-CSDN博客

2025 年 11 月 25 日

1
192.168.2.11
1
2
root
root

本机网段:192.168.2.209

1
ssh root@192.168.2.11
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
C:\WINDOWS\system32>ssh root@192.168.2.11
The authenticity of host '192.168.2.11 (192.168.2.11)' can't be established.
ED25519 key fingerprint is SHA256:wp0SoWD7JGL0Ce2wZcmRpRROghrusveXUGXs+Kj1ex4.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? y
Please type 'yes', 'no' or the fingerprint: yes
Warning: Permanently added '192.168.2.11' (ED25519) to the list of known hosts.
root@192.168.2.11's password:
Welcome to Ubuntu 20.04.2 LTS (GNU/Linux 5.4.70-00002-g5d231ff aarch64)

* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage

This system has been minimized by removing packages and content that are
not required on a system that users do not log into.

To restore this content, you can run the 'unminimize' command.
Last login: Tue Nov 25 13:08:34 2025

查看已识别的串口设备。

1
ls /dev/tty*
1
2
3
4
5
6
7
8
9
10
11
12
13
root@imx8mp:~# ls /dev/tty*
/dev/tty /dev/tty19 /dev/tty3 /dev/tty40 /dev/tty51 /dev/tty62 /dev/ttyp6 /dev/ttyS2 /dev/ttysWK22
/dev/tty0 /dev/tty2 /dev/tty30 /dev/tty41 /dev/tty52 /dev/tty63 /dev/ttyp7 /dev/ttyS3 /dev/ttysWK23
/dev/tty1 /dev/tty20 /dev/tty31 /dev/tty42 /dev/tty53 /dev/tty7 /dev/ttyp8 /dev/ttysWK00 /dev/ttysWK30
/dev/tty10 /dev/tty21 /dev/tty32 /dev/tty43 /dev/tty54 /dev/tty8 /dev/ttyp9 /dev/ttysWK01 /dev/ttysWK31
/dev/tty11 /dev/tty22 /dev/tty33 /dev/tty44 /dev/tty55 /dev/tty9 /dev/ttypa /dev/ttysWK02 /dev/ttysWK32
/dev/tty12 /dev/tty23 /dev/tty34 /dev/tty45 /dev/tty56 /dev/ttymxc1 /dev/ttypb /dev/ttysWK03 /dev/ttysWK33
/dev/tty13 /dev/tty24 /dev/tty35 /dev/tty46 /dev/tty57 /dev/ttyp0 /dev/ttypc /dev/ttysWK10 /dev/ttyUSB0
/dev/tty14 /dev/tty25 /dev/tty36 /dev/tty47 /dev/tty58 /dev/ttyp1 /dev/ttypd /dev/ttysWK11
/dev/tty15 /dev/tty26 /dev/tty37 /dev/tty48 /dev/tty59 /dev/ttyp2 /dev/ttype /dev/ttysWK12
/dev/tty16 /dev/tty27 /dev/tty38 /dev/tty49 /dev/tty6 /dev/ttyp3 /dev/ttypf /dev/ttysWK13
/dev/tty17 /dev/tty28 /dev/tty39 /dev/tty5 /dev/tty60 /dev/ttyp4 /dev/ttyS0 /dev/ttysWK20
/dev/tty18 /dev/tty29 /dev/tty4 /dev/tty50 /dev/tty61 /dev/ttyp5 /dev/ttyS1 /dev/ttysWK21

通常,系统串口名称如 /dev/ttyS0, /dev/ttyS1(原生串口)或 /dev/ttyUSB0, /dev/ttyUSB1(USB转串口)。您需要查看这些设备哪个存在。

拔掉插在 ip 盒子上的顶棚灯转USB串口线,再次执行命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
root@imx8mp:~# ls /dev/tty*
/dev/tty /dev/tty19 /dev/tty3 /dev/tty40 /dev/tty51 /dev/tty62 /dev/ttyp6 /dev/ttyS2 /dev/ttysWK22
/dev/tty0 /dev/tty2 /dev/tty30 /dev/tty41 /dev/tty52 /dev/tty63 /dev/ttyp7 /dev/ttyS3 /dev/ttysWK23
/dev/tty1 /dev/tty20 /dev/tty31 /dev/tty42 /dev/tty53 /dev/tty7 /dev/ttyp8 /dev/ttysWK00 /dev/ttysWK30
/dev/tty10 /dev/tty21 /dev/tty32 /dev/tty43 /dev/tty54 /dev/tty8 /dev/ttyp9 /dev/ttysWK01 /dev/ttysWK31
/dev/tty11 /dev/tty22 /dev/tty33 /dev/tty44 /dev/tty55 /dev/tty9 /dev/ttypa /dev/ttysWK02 /dev/ttysWK32
/dev/tty12 /dev/tty23 /dev/tty34 /dev/tty45 /dev/tty56 /dev/ttymxc1 /dev/ttypb /dev/ttysWK03 /dev/ttysWK33
/dev/tty13 /dev/tty24 /dev/tty35 /dev/tty46 /dev/tty57 /dev/ttyp0 /dev/ttypc /dev/ttysWK10
/dev/tty14 /dev/tty25 /dev/tty36 /dev/tty47 /dev/tty58 /dev/ttyp1 /dev/ttypd /dev/ttysWK11
/dev/tty15 /dev/tty26 /dev/tty37 /dev/tty48 /dev/tty59 /dev/ttyp2 /dev/ttype /dev/ttysWK12
/dev/tty16 /dev/tty27 /dev/tty38 /dev/tty49 /dev/tty6 /dev/ttyp3 /dev/ttypf /dev/ttysWK13
/dev/tty17 /dev/tty28 /dev/tty39 /dev/tty5 /dev/tty60 /dev/ttyp4 /dev/ttyS0 /dev/ttysWK20
/dev/tty18 /dev/tty29 /dev/tty4 /dev/tty50 /dev/tty61 /dev/ttyp5 /dev/ttyS1 /dev/ttysWK21

这个差别,这就是顶棚灯串口设备路径了:

1
/dev/ttyUSB0

2025 年 11 月 20 日

IP机顶盒_百度百科

(99+ 封私信 / 82 条消息) 公网ip盒子? - 知乎

Figma

2025 年 11 月 18 日

Home – Figma

没设计也能很漂亮,非常适合独立开发:Trae + 飞个马MCP 因为团队规模比较小,没有专门的设计师,就尝试使用Trae - 掘金

Introduction | Developer Docs

image-20251118170525775

1
figd_HoRi7tW3q5MlBxcoxP5N3UUVBERHSiAEtfXcMH9x

模型上下文协议(MCP) - 文档 - TRAE CN

Node 版本过低,得重新下载新版本。

1
2
3
4
5
6
7
8
9
10
D:\softWare\nvm\nvm\v18.16.1\node_cache\_npx\e05028111420be70\node_modules\sharp\lib\sharp.js:120
throw new Error(help.join('\n'));
^

Error: Could not load the "sharp" module using the win32-x64 runtime
Possible solutions:
- Please upgrade Node.js:
Found 16.19.0
Requires ^18.17.0 || ^20.3.0 || >=21.0.0
- Consult the installation documentation:
1
nvm list					   #查看已安装的node版本号
1
nvm list available			   #查看可安装的node版本
1
nvm install 【版本号】			#安装指定版本的node
1
nvm use 【版本号】				#使用指定版本的node

又出问题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
D:\softWare\nvm\nvm\v18.16.1\node_cache\_npx\e05028111420be70\node_modules\sharp\lib\sharp.js:120
throw new Error(help.join('\n'));
^

Error: Could not load the "sharp" module using the win32-x64 runtime
Possible solutions:
- Ensure optional dependencies can be installed:
npm install --include=optional sharp
- Ensure your package manager supports multi-platform installation:
See https://sharp.pixelplumbing.com/install#cross-platform
- Add platform-specific dependencies:
npm install --os=win32 --cpu=x64 sharp
- Consult the installation documentation:
See https://sharp.pixelplumbing.com/install
at Object.<anonymous> (D:\softWare\nvm\nvm\v18.16.1\node_cache\_npx\e05028111420be70\node_modules\sharp\lib\sharp.js:120:9)
at Module._compile (node:internal/modules/cjs/loader:1706:14)
at Object..js (node:internal/modules/cjs/loader:1839:10)
at Module.load (node:internal/modules/cjs/loader:1441:32)
at Function._load (node:internal/modules/cjs/loader:1263:12)
at TracingChannel.traceSync (node:diagnostics_channel:328:14)
at wrapModuleLoad (node:internal/modules/cjs/loader:237:24)
at Module.require (node:internal/modules/cjs/loader:1463:12)
at require (node:internal/modules/helpers:147:16)
at Object.<anonymous> (D:\softWare\nvm\nvm\v18.16.1\node_cache\_npx\e05028111420be70\node_modules\sharp\lib\constructor.js:10:1)

Node.js v22.21.1
1
npm install --include=optional sharp
1
npm install --os=win32 --cpu=x64 sharp

怎么还是报错。。

Docker

2025 年 12 月 3 日

1
WORKDIR 工作目录,是容器内部的工作目录,还是宿主机?

WORKDIR 的作用范围

1
2
3
# 这个路径指的是容器内部的路径
WORKDIR /app
# 相当于在容器内部执行:cd /app
指令 作用范围 示例 效果
WORKDIR 容器内部 WORKDIR /app 设置容器内工作目录
-v 挂载 宿主机↔容器 -v /host:/container 目录映射
COPY 宿主机→容器 COPY . . 文件复制
-w 参数 覆盖WORKDIR -w /newdir 运行时设置工作目录

1
我不明白啊,根据镜像文件构建容器,那这个容器内部的目录结构是什么样子,我完全没概念。

基础镜像的目录结构

python:3.9-slim为例,它内部的目录结构是这样的:

1
2
3
4
5
6
7
8
9
10
11
/                 (根目录)
├── bin/ (可执行文件)
├── etc/ (配置文件)
├── home/ (用户目录)
├── root/ (root用户目录)
├── tmp/ (临时文件)
├── usr/ (用户程序)
│ ├── bin/ (用户命令)
│ └── local/ (本地安装程序)
├── var/ (可变数据)
└── opt/ (可选应用软件)

1
都说Dockerfile镜像文件第一行必须是From指定根据哪个镜像构建,那到底应该根据哪个镜像构建呢,这里编码有什么名堂吗,基于不同的镜像构建又有什么区别?

基础镜像的选择策略。

  1. 官方镜像 vs 非官方镜像
1
2
3
4
5
6
7
8
# ✅ 推荐:官方镜像
FROM python:3.9-slim
FROM node:16-alpine
FROM nginx:latest

# ⚠️ 谨慎:非官方镜像(可能存在安全风险)
FROM someuser/random-image
FROM unknown-registry/app
  1. 不同标签的区别
1
2
3
4
5
6
7
8
9
# Python镜像的不同版本
FROM python:3.9 # 完整版,包含编译工具(约900MB)
FROM python:3.9-slim # 精简版,只含运行时(约150MB)
FROM python:3.9-alpine # 最小版,基于Alpine(约50MB)

# Node.js镜像的不同版本
FROM node:16 # 完整版
FROM node:16-slim # 精简版
FROM node:16-alpine # Alpine版

📊 主流基础镜像对比

镜像类型 大小 特点 适用场景 示例
完整版 300MB-1GB 包含完整工具链 开发环境、需要编译 python:3.9
精简版 100-300MB 只含运行时 生产环境 python:3.9-slim
Alpine版 5-100MB 极简、安全 微服务、资源敏感 python:3.9-alpine
Distroless 20-50MB 无shell、最安全 安全要求高的生产 gcr.io/distroless/python3

2025 年 12 月 1 日

Docker入门到实践(一)docker简介和安装docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及 - 掘金

2025 年 11 月 21 日

基于Docker、JenKins、Nginx、Harbor、Github及腾讯云服务器搭建个人博客基于Docker、Je - 掘金

2025 年 11 月 20 日

查找并删除特定镜像。

1
docker images | grep "nginx" | awk '{print $3}' | xargs docker rmi
  • docker images:列出所有镜像
  • grep "nginx":过滤出包含”nginx”的行
  • awk '{print $3}':提取第三列(镜像ID)
  • xargs docker rmi:将镜像ID作为参数传递给docker rmi删除

xargs命令从标准输入读取数据,并将这些数据作为参数传递给指定的命令。在上面的命令中,它用于将前一个命令(如awk、grep等)输出的结果(通常是多个ID)作为参数传递给docker命令(如rmi、stop、rm等)。

这样可以避免手动逐个处理,实现批量操作

1
2
3
4
5
6
7
8
9
10
11
# 查找并删除特定镜像
docker images | grep "nginx" | awk '{print $3}' | xargs docker rmi

# 停止所有运行中的容器
docker ps -q | xargs docker stop

# 删除所有退出状态的容器
docker ps -a | grep "Exited" | awk '{print $1}' | xargs docker rm

# 查看容器占用的端口
docker ps --format "table {{.Names}}\t{{.Ports}}" | grep "80"

Docker常用命令大全:一篇文章让你掌握所有技巧-腾讯云开发者社区-腾讯云

管道操作,批量操作,条件过滤与格式化,设置别名和快捷命令,脚本自动化管理,常见问题排查命令……

组合命令和高效技巧,等今后用到了再过头来总结吧。

2025 年 11 月 19 日

Linux K8S(Kubernetes)常用命令一个完备的分布式系统支撑平台,具有完备的集群管理能力,多扩多层次的安全 - 掘金

开发运维们,肯巴尔斯(K8s)驾到。对于K8s这种涉及领域广、知识面杂的技术,一上来就想抠细节、要吃透,多少有点盲目。本 - 掘金

最详细的 K8S 学习笔记总结(2021最新版)虽然 Docker 已经很强大了,但是在实际使用上还是有诸多不便,比如集 - 掘金

2025 年 11 月 18 日

有哪些常见的Docker容器操作命令?掌握这些Docker命令,让容器管理事半功倍-腾讯云开发者社区-腾讯云

Docker入门到实践(三)Dockerfile解析与镜像制作在上一篇文章中已经介绍了 docker 常用命令的讲解, - 掘金

最详细的 K8S 学习笔记总结(2021最新版)虽然 Docker 已经很强大了,但是在实际使用上还是有诸多不便,比如集 - 掘金

下午重温了监测系统项目架构,目前需要学习的主要是 Docker,串口屏。

Docker命令可以按功能分为五大类:

  1. 镜像管理命令:pull、push、build、images、rmi等
  2. 容器生命周期命令:run、start、stop、rm、exec等
  3. 网络管理命令:network create、network ls等
  4. 存储管理****命令:volume create、volume ls等
  5. 系统信息命令:info、stats、system等

远程调试

2025 年 11 月 24 日

周五调IO卡,自己鼓捣了一下午都没能ping通,晚上七点半才蹭车回到家,周六还得去一趟协助调试顶棚灯。

周六,中午十二点半起身出发,一点钟开始调试,他妈的一直到晚八点钟才作罢,调试的结果还不在预期,浪费我宝贵的周末时间。

周日休息。

周一。

2025 年 11 月 21 日

周三,费显调试结束,晚六点后离开前才确认声光报警有问题。

周四,调IO调不了,反复确认IO卡不是串口就是网口,就是网口,掰扯了三天总算罢休,中午不调了,得等之后再调试IO卡和顶棚灯。

周五,不清楚今中午还能调什么。

2025 年 11 月 20 日

中午回去得取个邮政快递,接下来的安排基本跟昨天一样,希望今天中午的调试工作能顺利完成吧,就能空余出时间来由自己折腾。

2025 年 11 月 19 日

早上十点钟下楼,回家里躺一会儿,煮面吃,十二点钟打个车子去车道。

1
2
3
4
5
6
7
8
IO读
发送到设备:01 02 00 00 00 08 79 CC
01 02 00 C8 00 08 F8 32
设备回复:01 02 01 02 20 49,第四个字节(02)是返回的设备IO状态
IO写
发送到设备:01 05 00 00 FF 00 8C 3A,第四个字节(00)表示第一位,第N位=N-1,第五(FF)表示高电平,(00)表示低电平。
01 05 00 00 FF 00 8C 3A
01 05 00 64 00 00 8C 15
1
@matty 现场恢复后测过了,费显正常,但声光报警不工作,之后他们得检查下

下午六点多钟蹭车回单位,坐电梯直接上一层大厅回家。

2025 年 11 月 18 日

自上周三开始,周五、周六、周日、周一各跑了一趟去车道协助远程调试云收费系统,连续几天都没睡过安稳的午觉。

周三跟着维修人员,中午用串口线调试费用显示器,晚六点前回到单位,他们还发了个动态库给我。

周四在工位坐了一天,下午又接到通知让周五跟他们一块儿去车道现场,周五从早去到现场一直到晚六点半才回单位,直接回家了。

周五就没有调试任务,得周末才开始调。

周六,早十点钟打车到车道现场,一直到下午快六点钟才调试结束,接内网看ETC工控机ip搞出了一堆破事,调试效率太低。

周日,中午一点钟临时协商好去车道现场,把刚买回的键盘取到手后就能配合调试,结果一搞就到了晚八点钟才回,车牌识别还没测完。

周一,早十一点钟打个车子现场,半小时后封车道协调完毕,中午十二点去餐厅吃了顿饱饭,两点前结束车牌识别调试,调试费额显示器的时候因为串口线有问题,从两点半折腾到下午四点半才确定是芯片老旧的问题,买根新线再试应该没问题。

周二,估计不去了,妈的昨晚又给我发消息问今天能不能测,新线上午到不了就周三调试,我估摸着肯定到不了了,京东快递撑死了一天内给送到,得今天晚些甚至明天早上才能取到了。

亲测有效!WIN10系统PL2303 USB转串口驱动兼容问题解决指南-CSDN博客

1
接下来要调io,需要一块铁板压地感,有时垃圾斗就可以,最好一块大一点的铁,比如栏杆机的门,或机箱的侧面铁板之类的。

单片机

2025 年 11 月 24 日

什么是单片机?

1
单片机(Single-Chip Microcomputer)是一种集成电路芯片,是采用超大规模集成电路技术把具有数据处理能力的中央处理器CPU、随机存储器RAM、只读存储器ROM、多种I/O口和中断系统、定时器/计数器等功能(可能还包括显示驱动电路、脉宽调制电路、模拟多路转换器、A/D转换器等电路)集成到一块硅片上构成的一个小而完善的微型计算机系统,在工业控制领域广泛应用。从上世纪80年代,由当时的4位、8位单片机,发展到现在的300M的高速单片机。
1
单片机又称单片微控制器,它不是完成某一个逻辑功能的芯片,而是把一个计算机系统集成到一个芯片上。相当于一个微型的计算机,和计算机相比,单片机只缺少了I/O设备。概括的讲:一块芯片就成了一台计算机。它的体积小、质量轻、价格便宜、为学习、应用和开发提供了便利条件。同时,学习使用单片机是了解计算机原理与结构的最佳选择。

什么是单片机?它由哪几部分组成?组成、特点与发展趋势解读

单片机的应用领域。

由于单片机具有体积小、功耗低、可靠性高、开发方便等特点,因此被广泛应用于各个领域:

1. 智能仪表:如电能表、水表、气表等,用于实现数据的采集、处理和显示。

2. 实时测控:如温度控制、压力控制等工业测控系统。

3. 通讯设备:如手机、无线对讲机等通讯设备中,用于实现信号的处理和传输。

4. 导航系统:如GPS导航系统,用于实现定位和导航功能。

5. 家用电器:如空调、冰箱、洗衣机等家用电器中,用于实现智能化控制和节能管理。


1
串口屏开发需要用到 USART HMI 编辑器,那么单片机开发呢,常用的编辑器有哪些?

单片机开发的核心是集成开发环境(IDE),它把代码编辑、编译、调试、烧录等功能集成在一起,下面这个表格汇总了不同单片机常用的主流IDE,方便你快速了解。

单片机系列/架构 主流IDE选择
STM32 (ARM Cortex-M) STM32CubeIDE, Keil MDK-ARM, IAR Embedded Workbench
ESP32/ESP8266 ESP-IDF, Arduino IDE, PlatformIO (支持多种框架)
51单片机 Keil C51
PIC单片机 MPLAB X IDE
AVR单片机 Microchip Studio (原Atmel Studio), Arduino IDE

嵌入式开发_百度百科

一个典型的嵌入式系统通常包括硬件和软件两部分:

  • 硬件:以嵌入式微处理器(如ARM Cortex-M系列芯片)为核心,配以存储器、输入/输出设备及电源管理等。
  • 软件:包括可选的嵌入式操作系统(如FreeRTOS、Linux)和专用的应用程序,这些软件通常被固化在存储芯片中。

串口屏

2025 年 12 月 4 日

基本通信协议格式。

  1. 指令格式
1
2
3
4
5
6
7
# 基本格式:指令 + 结束符
"指令内容\xFF\xFF\xFF"

# 示例:
"page 1\xFF\xFF\xFF" # 页面切换
"t0.txt=\"Hello World\"\xFF\xFF\xFF" # 设置文本
"j0.val=50\xFF\xFF\xFF" # 设置进度条值
  1. 通信方向
1
2
Python程序 → 串口屏:发送控制指令
串口屏 → Python程序:发送触摸事件、传感器数据等

📋 完整协议规范

下行协议(Python → 串口屏)

页面控制指令

1
2
3
4
5
6
# 页面跳转
"page 0\xFF\xFF\xFF" # 跳转到页面0
"page main\xFF\xFF\xFF" # 跳转到名为main的页面

# 页面刷新
"ref 0\xFF\xFF\xFF" # 刷新页面0

控件操作指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 文本控件
"t0.txt=\"温度:25℃\"\xFF\xFF\xFF" # 设置文本内容
"t1.txt=\"湿度:60%\"\xFF\xFF\xFF"

# 进度条/仪表控件
"j0.val=75\xFF\xFF\xFF" # 设置进度条值(0-100)
"z0.val=50\xFF\xFF\xFF" # 设置仪表值

# 按钮控件
"b0.txt=\"确定\"\xFF\xFF\xFF" # 设置按钮文本
"b0.bco=63488\xFF\xFF\xFF" # 设置按钮背景色(RGB565)

# 显示/隐藏控件
"vis t0,1\xFF\xFF\xFF" # 显示文本控件t0
"vis b1,0\xFF\xFF\xFF" # 隐藏按钮b1

系统指令

1
2
3
4
5
6
7
8
9
# 背光控制
"dim=100\xFF\xFF\xFF" # 背光亮度100%
"sleep=0\xFF\xFF\xFF" # 禁止休眠

# 声音提示
"beep\xFF\xFF\xFF" # 蜂鸣器响一声

# 系统重启
"restart\xFF\xFF\xFF" # 重启串口屏

上行协议(串口屏 → Python)

触摸事件格式

1
2
3
4
5
6
7
8
# 触摸按下事件:65 PP CC 01 FF FF FF
# 触摸释放事件:65 PP CC 00 FF FF FF

# 其中:
# 65: 固定头(触摸事件)
# PP: 页面ID
# CC: 控件ID
# 01/00: 状态(01=按下, 00=释放)

数据上报格式

1
2
3
4
# 控件值变化上报
"t0.txt=\"新值\"\xFF\xFF\xFF" # 文本内容变化
"j0.val=50\xFF\xFF\xFF" # 进度条值变化
"n0.val=123\xFF\xFF\xFF" # 数值变化

2025 年 12 月 1 日

怪不得本机 Python 监听程序失效,在编辑界面的按钮按下事件里勾选发送键值选项,模拟器在用户MCU输入选项下的串口配置中启动。

这样在模拟器点击按钮后就能接收到返回的数据,如图所示:

image-20251201091736358

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
def advanced_button_listener():
"""监听按钮点击事件的完整程序"""
try:
# 连接COM4(模拟器连接COM3)
ser = serial.Serial('COM4', 115200, timeout=1)
print("🎯 按钮监听程序已启动")
print("📡 监听COM4,等待COM3的模拟器信号")
print("请在模拟器中点击配置了'触摸发送'的按钮")
print("按Ctrl+C停止监听\n")
print("-" * 60)

receive_count = 0

while True:
# 检查是否有数据
if ser.in_waiting > 0:
data = ser.read(ser.in_waiting)
receive_count += 1
current_time = datetime.now().strftime("%H:%M:%S")

print(f"\n🕒 [{current_time}] 第{receive_count}次收到数据:")

# 显示十六进制
hex_data = ' '.join([f'{b:02X}' for b in data])
print(f"🔢 原始数据: {hex_data}")

# 尝试解析为文本
try:
# 尝试UTF-8解码
text_utf8 = data.decode('utf-8', errors='ignore')
# 尝试GB2312解码(中文常用)
text_gb = data.decode('gb2312', errors='ignore')

# 显示可读部分
clean_utf8 = ''.join(c if 32 <= ord(c) <= 126 else f'[{ord(c):02X}]' for c in text_utf8)
clean_gb = ''.join(c if 32 <= ord(c) <= 126 else f'[{ord(c):02X}]' for c in text_gb)

if clean_utf8.strip():
print(f"📝 UTF-8文本: {clean_utf8}")
if clean_gb.strip() and clean_gb != clean_utf8:
print(f"📝 GB2312文本: {clean_gb}")
except Exception as e:
print(f"❌ 解码错误: {e}")

# 解析标准触摸事件 (65 PP PP CC CC SS FF FF FF)
if len(data) >= 6 and data[0] == 0x65:
page_id = (data[1] << 8) | data[2]
control_id = (data[3] << 8) | data[4]
state = data[5]

if state == 0x01:
print(f"🟢 按钮按下! 页面{page_id} 控件{control_id}")
elif state == 0x00:
print(f"🔴 按钮释放! 页面{page_id} 控件{control_id}")
else:
print(f"❓ 未知状态: {state:02X}")

# 检查是否包含常见指令
if b'button' in data.lower() or b'click' in data.lower():
print("🎯 检测到按钮相关指令!")

print("-" * 50)

time.sleep(0.05) # 更快的响应

except KeyboardInterrupt:
print(f"\n🛑 监听结束,共收到 {receive_count} 次数据")
except Exception as e:
print(f"❌ 错误: {e}")
finally:
if 'ser' in locals() and ser.is_open:
ser.close()
print("🔒 串口已关闭")
1
2
3
4
🕒 [09:13:24] 第3次收到数据:
🔢 原始数据: 65 00 07 01 FF FF FF
📝 UTF-8文本: e[00][07][01]
❓ 未知状态: FF

还得优化下 Python 对串口发来指令的分析。

1
2
3
4
5
6
7
8
9
10
Traceback (most recent call last):
File "D:\dev\Pycharm\plugins\python-ce\helpers\pydev\_pydevd_bundle\pydevd_pep_669_tracing.py", line 238, in py_start_callback
if not is_thread_alive(thread):
~~~~~~~~~~~~~~~^^^^^^^^
File "D:\dev\Pycharm\plugins\python-ce\helpers\pydev\_pydev_bundle\pydev_is_thread_alive.py", line 18, in is_thread_alive
return t.isAlive()
^^^^^^^^^
AttributeError: 'ReaderThread' object has no attribute 'isAlive'. Did you mean: 'is_alive'?
Traceback (most recent call last):
File "D:\dev\Pycharm\plugins\python-ce\helpers\pydev\_pydevd_bundle\pydevd_pep_669_tracing.py", line 238, in

PyCharm 调试器内部代码使用了旧的线程方法名 isAlive(),但新版本Python中线程对象使用 is_alive()。

解决方案:以普通模式运行,不要使用调试模式,直接点击运行按钮。

妈的。

完整的触摸事件监听程序如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
import serial
import time
from datetime import datetime


# 触摸事件解析程序
def parse_touch_data(data):
print("接收到的数据:" + str(data))

if len(data) >= 7 and data[0] == 0x65 and data[-3:] == b'\xFF\xFF\xFF':
print("数据格式正确,开始解析...")
page_id = data[1] # 页面ID
control_id = data[2] # 控件ID
state = data[3] # 状态:01=按下,00=释放/弹起

return {
"page": page_id,
"control": control_id,
"state": "按下" if state == 0x01 else "弹起"
}
return None

# 触摸事件监听程序
def display_touch_listener():
"""按照图片格式显示触摸事件"""
try:
ser = serial.Serial('COM4', 115200, timeout=1)
print("触摸事件监听程序已启动")
print("监听COM4端口,等待触摸事件...")
print("=" * 50)

while True:
if ser.in_waiting > 0:
# 检查是否有数据
data = ser.read(ser.in_waiting)
current_time = datetime.now().strftime("%H:%M:%S")
# 解析触摸事件
touch_info = parse_touch_data(data)
# 显示原始数据(十六进制格式)
hex_data = ' '.join([f'{b:02X}' for b in data])
print(f"[{current_time}] 收到数据: {hex_data}")
# 显示解析结果
if touch_info:
print(f"数据含义: 页面:{touch_info['page']} 控件:{touch_info['control']} {touch_info['state']}")
else:
# 如果不是标准触摸事件,尝试显示文本内容
try:
text = data.decode('utf-8', errors='ignore').strip()
if text:
print(f"文本内容: {text}")
except:
print("无法解析数据格式")

print("-" * 50)

time.sleep(0.05)

except KeyboardInterrupt:
print("\n监听结束")
except Exception as e:
print(f"错误: {e}")
finally:
if 'ser' in locals() and ser.is_open:
ser.close()


if __name__ == '__main__':
# 运行监听程序
display_touch_listener()

触摸监听后的反馈信息如下:

1
2
3
4
5
6
--------------------------------------------------
接收到的数据:b'e\x03\x07\x00\xff\xff\xff'
数据格式正确,开始解析...
[10:15:31] 收到数据: 65 03 07 00 FF FF FF
数据含义: 页面:3 控件:7 弹起
--------------------------------------------------

1
我这样刷新该页面后,返回上一页竟然把lastpage设置成当前页面了吗,原因是刷新页面后确实执行了页面离开事件,应该怎么优化一下,刷新后不影响返回到上一页?

很简单,在需要刷新的页面的页面离开事件中,不需要加以下代码即可:

1
lastpage=dp

以上是最简单粗暴的解决方法,还有比较优雅的解决方法,首先在 Program.s 全局文件中定义刷新标记。

1
int isRefreshing = 0     // 刷新标志

刷新按钮控件编码如下。

1
2
3
isRefreshing=1      // 设置刷新标志
page dp // 刷新当前页面
isRefreshing=0 // 清除刷新标志

该页面的页面离开事件这样写:

1
2
3
4
5
// 页面离开事件
if(isRefreshing==0)
{
lastpage=dp
}

1
使用Python能监听串口屏发来的触发事件信号,执行Python程序也能主动发送信号到串口屏,比如修改某个文本框的内容,那Python程序如何能在监听的过程中也能主动发送信号呢,怎么编码,反正两个Python程序不能监听同一个串口。

同一个串口不能同时被两个Python程序监听。解决方案是使用单程序多线程异步编程来实现同时监听和发送。以下是几种实现方式。

异步实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
import serial
import asyncio
import threading
import time

class AsyncHMICommunicator:
def __init__(self, port='COM4', baudrate=115200):
self.port = port
self.baudrate = baudrate
self.ser = None
self.running = False

async def start(self):
"""异步启动"""
try:
self.ser = serial.Serial(self.port, self.baudrate, timeout=0.1)
self.running = True

# 启动监听任务
asyncio.create_task(self._listener())

print(f"✅ 异步HMI通信器已启动")
return True

except Exception as e:
print(f"❌ 启动失败: {e}")
return False

async def _listener(self):
"""异步监听"""
while self.running:
try:
if self.ser.in_waiting > 0:
data = self.ser.read(self.ser.in_waiting)
await self._process_data(data)
await asyncio.sleep(0.01)
except Exception as e:
print(f"监听错误: {e}")
await asyncio.sleep(0.1)

async def send_command(self, command):
"""异步发送指令"""
if not self.running:
print("❌ 通信器未启动")
return

try:
if isinstance(command, str):
if not command.endswith('\xFF\xFF\xFF'):
command += '\xFF\xFF\xFF'
command = command.encode('gb2312')

self.ser.write(command)
print(f"✅ 指令已发送: {command}")

except Exception as e:
print(f"❌ 发送失败: {e}")

async def _process_data(self, data):
"""异步处理数据"""
hex_data = ' '.join([f'{b:02X}' for b in data])
print(f"📥 收到数据: {hex_data}")

# 异步使用示例
async def async_main():
hmi = AsyncHMICommunicator(port='COM4', baudrate=115200)

if await hmi.start():
# 发送初始指令
await hmi.send_command('t0.txt="异步通信测试"')

# 定时发送任务
async def periodic_sender():
counter = 0
while True:
await asyncio.sleep(3)
counter += 1
await hmi.send_command(f't1.txt="计数: {counter}"')

# 启动定时发送
asyncio.create_task(periodic_sender())

# 保持运行
await asyncio.Future() # 永久运行

# 运行异步程序
if __name__ == "__main__":
asyncio.run(async_main())
1
2
3
4
5
Traceback (most recent call last):
File "D:\Project\python\python通讯\python测试\asyncio.py", line 88, in <module>
asyncio.run(async_main())
^^^^^^^^^^^
AttributeError: module 'asyncio' has no attribute 'run' (consider renaming 'D:\Project\python\python通讯\python测试\asyncio.py' since it has the same name as the standard library module named 'asyncio' and prevents importing that standard library module)

文件命名与标准库asyncio模块冲突了,请 Python 文件重命名,不能使用asyncio.py这个文件名。

1
2
3
4
5
6
7
8
9
✅ 异步HMI通信器已启动
❌ 发送失败: 'gb2312' codec can't encode character '\xff' in position 15: illegal multibyte sequence
❌ 发送失败: 'gb2312' codec can't encode character '\xff' in position 14: illegal multibyte sequence
❌ 发送失败: 'gb2312' codec can't encode character '\xff' in position 14: illegal multibyte sequence
❌ 发送失败: 'gb2312' codec can't encode character '\xff' in position 14: illegal multibyte sequence
❌ 发送失败: 'gb2312' codec can't encode character '\xff' in position 14: illegal multibyte sequence
❌ 发送失败: 'gb2312' codec can't encode character '\xff' in position 14: illegal multibyte sequence
❌ 发送失败: 'gb2312' codec can't encode character '\xff' in position 14: illegal multibyte sequence
❌ 发送失败: 'gb2312' codec can't encode character '\xff' in position 14: illegal multibyte sequence

实现不了真正意义上的异步,本来 Python 程序就是作为后台服务的角色,只需要监控控件指令即可。

1
2
3
4
✅ HMI通信器已启动,端口: COM4
✅ 发送: t0.txt="Hello World"
✅ 发送: page 2
✅ 发送: t1.txt="计数: 1"

完整代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
import serial
import threading
import time
import queue

class FixedHMICommunicator:
def __init__(self, port='COM4', baudrate=115200):
self.port = port
self.baudrate = baudrate
self.ser = None
self.send_queue = queue.Queue()
self.running = False

def start(self):
"""启动通信"""
try:
self.ser = serial.Serial(self.port, self.baudrate, timeout=1)
self.running = True

# 启动监听线程
listener_thread = threading.Thread(target=self._listener, daemon=True)
listener_thread.start()

# 启动发送线程
sender_thread = threading.Thread(target=self._sender, daemon=True)
sender_thread.start()

print(f"✅ HMI通信器已启动,端口: {self.port}")
return True

except Exception as e:
print(f"❌ 启动失败: {e}")
return False

def _listener(self):
"""监听线程"""
while self.running:
try:
if self.ser and self.ser.in_waiting > 0:
data = self.ser.read(self.ser.in_waiting)
self._process_received_data(data)
time.sleep(0.01)
except Exception as e:
print(f"监听错误: {e}")
time.sleep(0.1)

def _sender(self):
"""发送线程"""
while self.running:
try:
if not self.send_queue.empty():
command = self.send_queue.get_nowait()
self._send_command(command)
time.sleep(0.01)
except Exception as e:
print(f"发送错误: {e}")
time.sleep(0.1)

def send_command(self, command):
"""发送指令"""
if self.running:
self.send_queue.put(command)
else:
print("❌ 通信器未启动")

def _send_command(self, command):
"""实际发送指令 - 修复编码问题"""
try:
if isinstance(command, str):
# 方法1:先编码文本部分,再添加字节结束符
if command.endswith('\xFF\xFF\xFF'):
# 如果字符串已经包含结束符,移除它们
command = command[:-9] # 移除最后9个字符(\xFF\xFF\xFF)

# 编码文本部分
text_encoded = command.encode('gb2312', errors='ignore')

# 添加字节结束符
full_command = text_encoded + b'\xFF\xFF\xFF'

self.ser.write(full_command)
print(f"✅ 发送成功: {command}")

elif isinstance(command, bytes):
# 如果已经是字节,直接发送
self.ser.write(command)
print(f"✅ 发送字节数据: {command.hex()}")

except Exception as e:
print(f"❌ 发送失败: {e}")

def send_command_safe(self, command_text):
"""安全的发送方法 - 推荐使用"""
try:
# 确保命令是字符串
if not isinstance(command_text, str):
command_text = str(command_text)

# 编码文本部分
text_bytes = command_text.encode('gb2312', errors='ignore')

# 添加结束符
full_command = text_bytes + b'\xFF\xFF\xFF'

if self.running and self.ser:
self.ser.write(full_command)
print(f"✅ 发送: {command_text}")
else:
print("❌ 通信器未就绪")

except Exception as e:
print(f"❌ 发送失败: {e}")

def _process_received_data(self, data):
"""处理接收数据"""
hex_data = ' '.join([f'{b:02X}' for b in data])
print(f"📥 收到: {hex_data}")

# 解析触摸事件
if len(data) >= 7 and data[0] == 0x65 and data[-3:] == b'\xFF\xFF\xFF':
page_id = data[1]
control_id = data[2]
state = data[3]
state_text = "按下" if state == 0x01 else "弹起"
print(f"🔄 触摸: 页面{page_id} 控件{control_id} {state_text}")

def stop(self):
"""停止"""
self.running = False
if self.ser:
self.ser.close()
print("🛑 通信器已停止")

# 使用示例
def main():
hmi = FixedHMICommunicator(port='COM4', baudrate=115200)

if hmi.start():
try:
# 使用安全的发送方法
hmi.send_command_safe('t0.txt="Hello World"')
time.sleep(3)

hmi.send_command_safe('page 2')
time.sleep(3)

# 持续发送测试
counter = 0
while True:
time.sleep(3)
counter += 1
hmi.send_command_safe(f't1.txt="计数: {counter}"')

except KeyboardInterrupt:
print("\n用户中断")
finally:
hmi.stop()

if __name__ == "__main__":
main()

我特么服了,记得二十八号晚些时候写过 Python 联调串口屏的经验来着,连接串口屏后同一时间 USART HMI 编辑器和 Python 程序只能有一个监听 COM5。

要么在模拟器连接到本机串口 COM5,在 USART HMI 软件接收串口屏发来的数据;要么使用 Python 监听串口屏端口 COM5,监听串口屏控件发来的指令,或者使用 Python 程序主动朝串口屏发送信号。

下午,优化完毕串口屏测试程序,接下来可以继续熟悉下串口屏控件编码,以及 Python 联调如何编码。

不想看了,看会儿 Docker。

2025 年 11 月 28 日

执行模拟串口通信测试程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
def test_virtual_ports():
"""测试虚拟串口通信"""
print("🔧 测试虚拟串口通信...")

try:
# 尝试向COM3发送数据(模拟器监听COM3)
ser_send = serial.Serial('COM3', 115200, timeout=1)
print("✅ 已连接COM3(发送端)")

# 尝试从COM4接收数据(Python监听COM4)
ser_recv = serial.Serial('COM4', 115200, timeout=1)
print("✅ 已连接COM4(接收端)")

# 发送测试数据
test_data = b"Hello from COM3!\xFF\xFF\xFF"
print(f"📤 向COM3发送: {test_data}")
ser_send.write(test_data)

# 等待并尝试从COM4接收
time.sleep(0.5)
if ser_recv.in_waiting > 0:
received = ser_recv.read(ser_recv.in_waiting)
print(f"📥 从COM4收到: {received}")
print("✅ 虚拟串口通信正常!")
else:
print("❌ 没有收到数据,虚拟串口可能有问题")

ser_send.close()
ser_recv.close()

except Exception as e:
print(f"❌ 测试失败: {e}")

测试结果:

1
2
3
4
5
6
send.py::test_virtual_ports PASSED                                       [100%]🔧 测试虚拟串口通信...
✅ 已连接COM3(发送端)
✅ 已连接COM4(接收端)
📤 向COM3发送: b'Hello from COM3!\xff\xff\xff'
📥 从COM4收到: b'Hello from COM3!\xff\xff\xff'
✅ 虚拟串口通信正常!

执行监听按钮点击事件完整程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
def advanced_button_listener():
"""监听按钮点击事件的完整程序"""
try:
# 连接COM4(模拟器连接COM3)
ser = serial.Serial('COM4', 115200, timeout=1)
print("🎯 按钮监听程序已启动")
print("📡 监听COM4,等待COM3的模拟器信号")
print("请在模拟器中点击配置了'触摸发送'的按钮")
print("按Ctrl+C停止监听\n")
print("-" * 60)

receive_count = 0

while True:
# 检查是否有数据
if ser.in_waiting > 0:
data = ser.read(ser.in_waiting)
receive_count += 1
current_time = datetime.now().strftime("%H:%M:%S")

print(f"\n🕒 [{current_time}] 第{receive_count}次收到数据:")

# 显示十六进制
hex_data = ' '.join([f'{b:02X}' for b in data])
print(f"🔢 原始数据: {hex_data}")

# 尝试解析为文本
try:
# 尝试UTF-8解码
text_utf8 = data.decode('utf-8', errors='ignore')
# 尝试GB2312解码(中文常用)
text_gb = data.decode('gb2312', errors='ignore')

# 显示可读部分
clean_utf8 = ''.join(c if 32 <= ord(c) <= 126 else f'[{ord(c):02X}]' for c in text_utf8)
clean_gb = ''.join(c if 32 <= ord(c) <= 126 else f'[{ord(c):02X}]' for c in text_gb)

if clean_utf8.strip():
print(f"📝 UTF-8文本: {clean_utf8}")
if clean_gb.strip() and clean_gb != clean_utf8:
print(f"📝 GB2312文本: {clean_gb}")
except Exception as e:
print(f"❌ 解码错误: {e}")

# 解析标准触摸事件 (65 PP PP CC CC SS FF FF FF)
if len(data) >= 6 and data[0] == 0x65:
page_id = (data[1] << 8) | data[2]
control_id = (data[3] << 8) | data[4]
state = data[5]

if state == 0x01:
print(f"🟢 按钮按下! 页面{page_id} 控件{control_id}")
elif state == 0x00:
print(f"🔴 按钮释放! 页面{page_id} 控件{control_id}")
else:
print(f"❓ 未知状态: {state:02X}")

# 检查是否包含常见指令
if b'button' in data.lower() or b'click' in data.lower():
print("🎯 检测到按钮相关指令!")

print("-" * 50)

time.sleep(0.05) # 更快的响应

except KeyboardInterrupt:
print(f"\n🛑 监听结束,共收到 {receive_count} 次数据")
except Exception as e:
print(f"❌ 错误: {e}")
finally:
if 'ser' in locals() and ser.is_open:
ser.close()
print("🔒 串口已关闭")

测试结果:

1
2
3
4
5
6
send.py::test_virtual_ports PASSED                                       [100%]🔧 测试虚拟串口通信...
✅ 已连接COM3(发送端)
✅ 已连接COM4(接收端)
📤 向COM3发送: b'Hello from COM3!\xff\xff\xff'
📥 从COM4收到: b'Hello from COM3!\xff\xff\xff'
✅ 虚拟串口通信正常!

Python 对COM3串口发送指令正常,监听COM4接受指令也正常,那就是模拟器操作控件肯定没有正常发送信号,估计得先编码控件。

这个AI的回复不能信服。

今天晚些时候再测。

2025 年 11 月 27 日

考虑到生产环境下的应用场景,使用 Python 脚本在后台监听模拟器或串口屏操作信号。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
def diagnose_connection():
print("=== 串口连接诊断 ===")

# 1. 列出所有可用串口
ports = serial.tools.list_ports.comports()
print("1. 可用串口列表:")
if not ports:
print(" ❌ 没有找到任何串口设备")
return False

for port in ports:
print(f" ✅ {port.device} - {port.description}")

# 2. 尝试连接指定串口
target_port = 'COM4' # 修改为您的端口号
print(f"\n2. 尝试连接 {target_port}")

try:
ser = serial.Serial(target_port, 9600, timeout=2)
print(f" ✅ 成功连接到 {target_port}")
ser.close()
return True
except Exception as e:
print(f" ❌ 连接失败: {e}")
return False


# 使用示例
if __name__ == "__main__":
# 修改为您的虚拟串口号(模拟器连接的那个端口)
# monitor_button_press(port='COM4', baudrate=115200)
diagnose_connection()
1
2
3
4
5
6
7
8
9
Traceback (most recent call last):
File "D:\Project\python\python通讯\python测试\test1127.py", line 85, in <module>
diagnose_connection()
~~~~~~~~~~~~~~~~~~~^^
File "D:\Project\python\python通讯\python测试\test1127.py", line 58, in diagnose_connection
ports = serial.tools.list_ports.comports()
^^^^^^^^^^^^
AttributeError: module 'serial' has no attribute 'tools'

1
2
import serial
from serial.tools import list_ports

2025 年 11 月 26 日

今天继续抽时间进一步优化串口屏调试。

串口助手软件(sscom)和屏幕联调 — 淘晶驰串口屏资料中心 1.1.0-2025-11-21 14:36:46 文档

1
USART(Universal Synchronous/Asynchronous Receiver/Transmitter)是一种全双工通用同步/异步串行收发模块,作为高度灵活的串行通信设备,支持相互独立的收发数据操作。其核心功能涵盖同步/异步双模式运行,可配置59位数据位及1-2位停止位,内置硬件奇偶校验、数据溢出检测、帧错误检测等机制,并具备三级中断支持。

妈的昨天看串口调试助手看走眼了,还以为有官方提供的虚拟串口设备,结果只是提供了同串口屏协作的调试助手而已。

这些调试助手包括但不限于:sscom,uartassist,xcom,llcom。


1
串口屏与单片机联调,与Arduino联调,与MicroPython联调,与Python联调,这些都是什么。

这些都是在不同环境下与淘晶驰串口屏进行通信和调试的场景。它们的目标一致:让外部设备或程序能够控制串口屏的显示,并接收串口屏的触摸等事件。但使用的工具和编程语言不同。

单片机:如STM32、51系列等,是嵌入式设备的核心控制芯片。

Arduino:一种开源的电子原型平台,使用C++风格的Arduino语言,编程简单。

MicroPython:是运行在微控制器上的Python语言。

Python:这里通常指在电脑上运行的Python程序。

本质上都是通过串口协议与屏幕通信,只是编程语言和运行环境不同。

晚十点半,下载周一编码完成的控件到串口屏,执行操作发送到本机模拟器和串口屏,成功了,他妈的联调成功。

本机启动模拟器,确保正确下载完成控件程序到串口屏后,HMI 提供的模拟器调试键盘输入,能正确操作连接在本机上的串口屏。

停止模拟器调试,只是单纯把串口屏插在本机上,我的串口屏联机用到的串口号是COM5,注意这里只有一个串口号,那就是串口屏的串口号,启动Python程序直接发送信号到COM5,翻页成功。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import serial #导入模块

# print(serial.__file__)
# print(dir(serial))
# print(serial.file)

try:
ser=serial.Serial(port="COM5",baudrate=115200,timeout=5)
command = b"page page0\xff\xff\xff"
ser.write(command)
print("发送页面切换指令")

except Exception as e:
print("---异常---:",e)

同样的,改写文本字符也成功了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import serial #导入模块

# print(serial.__file__)
# print(dir(serial))
# print(serial.file)

try:
#端口,GNU / Linux上的/ dev / ttyUSB0 等 或 Windows上的 COM1 等
#波特率,标准值:2400 4800 9600 19200 38400 57600 115200 230400 256000 512000 921600
#超时设置,None:永远等待操作,0为立即返回请求结果,其他值为等待超时时间(单位为秒)
# 打开串口,并得到串口对象
ser=serial.Serial(port="COM5",baudrate=115200,timeout=5)
print("串口详情参数:", ser)
# 写数据
#注意:编码应与工程的编码一致(设备-字符编码,如果usarthmi中设置的为utf-8,则这里也填写utf8,如果usarthmi中设置的为GB2312,则这里也填写GB2312也可以填写GBK)
#result=ser.write("page page0".encode("utf-8"))
#result=ser.write("page page2".encode("GB2312"))
result=ser.write("t0.txt=\"淘晶驰电子!\"".encode("GB2312"))
# 发送结束符
ser.write(bytes.fromhex('ff ff ff'))

ser.close()#关闭串口

except Exception as e:
print("---异常---:",e)

今天的调试结束。

2025 年 11 月 25 日

淘晶驰资料中心 — 淘晶驰串口屏资料中心 1.1.0-2025-11-21 14:36:46 文档

与 Python 联调。

Python 发送数据给屏幕工程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import serial #导入模块
try:
#端口,GNU / Linux上的/ dev / ttyUSB0 等 或 Windows上的 COM1 等
#波特率,标准值:2400 4800 9600 19200 38400 57600 115200 230400 256000 512000 921600
#超时设置,None:永远等待操作,0为立即返回请求结果,其他值为等待超时时间(单位为秒)
# 打开串口,并得到串口对象
ser=serial.Serial(port="COM9",baudrate=115200,timeout=5)
print("串口详情参数:", ser)
# 写数据
#注意:编码应与工程的编码一致(设备-字符编码,如果usarthmi中设置的为utf-8,则这里也填写utf8,如果usarthmi中设置的为GB2312,则这里也填写GB2312也可以填写GBK)
#result=ser.write("page page0".encode("utf-8"))
#result=ser.write("page page2".encode("GB2312"))
result=ser.write("t0.txt=\"淘晶驰电子!\"".encode("GB2312"))
# 发送结束符
ser.write(bytes.fromhex('ff ff ff'))

ser.close()#关闭串口

except Exception as e:
print("---异常---:",e)

这段代码是一个串口通信程序,主要用于与淘晶驰串口屏(HMI人机界面)进行通信和控制。

代码执行流程:

  1. 打开COM9串口
  2. 发送显示文本指令
  3. 发送结束标志
  4. 关闭串口

Python 获取电脑状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
import time
import psutil
import serial

time.sleep(3)

# function of Get CPU State;
def getCPUstate(interval=1):
return (" CPU: " + str(psutil.cpu_percent(interval)) + "%")


# function of Get Memory
def getMemorystate():
phymem = psutil.virtual_memory()
line = "Memory: %5s%% %6s/%s" % (
phymem.percent,
str(int(phymem.used / 1024 / 1024)) + "M",
str(int(phymem.total / 1024 / 1024)) + "M"
)
return line


def bytes2human(n):
"""
>>> bytes2human(10000)
'9.8 K'
>>> bytes2human(100001221)
'95.4 M'
"""
symbols = ('K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y')
prefix = {}
for i, s in enumerate(symbols):
prefix[s] = 1 << (i + 1) * 10
for s in reversed(symbols):
if n >= prefix[s]:
value = float(n) / prefix[s]
return '%.2f %s' % (value, s)
return '%.2f B' % (n)


def poll(interval):
"""Retrieve raw stats within an interval window."""
tot_before = psutil.net_io_counters()
pnic_before = psutil.net_io_counters(pernic=True)
# sleep some time
time.sleep(interval)
tot_after = psutil.net_io_counters()
pnic_after = psutil.net_io_counters(pernic=True)
# get cpu state
cpu_state = getCPUstate(interval)
# get memory
memory_state = getMemorystate()
return (tot_before, tot_after, pnic_before, pnic_after, cpu_state, memory_state)


def refresh_window(tot_before, tot_after, pnic_before, pnic_after, cpu_state, memory_state):

str = "t0.txt=\"" + time.asctime() + " | " + cpu_state + " | " + memory_state + "\""

serial.write(str.encode("GB2312"))
serial.write(bytes.fromhex('ff ff ff'))

nic_names = pnic_after.keys()
# nic_names.sort(key=lambda x: sum(pnic_after[x]), reverse=True)

for name in nic_names:
# 有线一般是以太网,无线则是WLAN,具体请查看"控制面板\网络和 Internet\网络连接"
if name == "以太网":
stats_before = pnic_before[name]
stats_after = pnic_after[name]
templ = "%-15s %15s %15s"
str = "t1.txt=\"上传数据总量" + bytes2human(stats_after.bytes_sent) + " 上传速度:" + bytes2human(stats_after.bytes_sent - stats_before.bytes_sent) + '/s\"'
serial.write(str.encode("GB2312"))
serial.write(bytes.fromhex('ff ff ff'))
str = "t2.txt=\"下载数据总量" + bytes2human(stats_after.bytes_recv) + " 下载速度:" + bytes2human(stats_after.bytes_recv - stats_before.bytes_recv) + '/s\"'
serial.write(str.encode("GB2312"))
serial.write(bytes.fromhex('ff ff ff'))


try:
interval = 0
portx = "COM9"
bauds = 115200
timex = 5
serial = serial.Serial(port=portx,baudrate=bauds,timeout=timex)
while 1:
args = poll(interval)
refresh_window(*args)
interval = 1
except (KeyboardInterrupt, SystemExit):
pass
1
pip install psutil
1
pip install serial
1
pip uninstall serial pyserial
1
2
print(serial.__file__)
print(dir(serial))
1
2
3
C:\Users\Lenovo\AppData\Local\Programs\Python\Python314\Lib\site-packages\serial\__init__.py

['CR', 'EIGHTBITS', 'FIVEBITS', 'LF', 'PARITY_EVEN', 'PARITY_MARK', 'PARITY_NAMES', 'PARITY_NONE', 'PARITY_ODD', 'PARITY_SPACE', 'PortNotOpenError', 'SEVENBITS', 'SIXBITS', 'STOPBITS_ONE', 'STOPBITS_ONE_POINT_FIVE', 'STOPBITS_TWO', 'Serial', 'SerialBase', 'SerialException', 'SerialTimeoutException', 'Timeout', 'VERSION', 'XOFF', 'XON', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', '__version__', 'absolute_import', 'basestring', 'importlib', 'io', 'iterbytes', 'os', 'protocol_handler_packages', 'serial_for_url', 'serialutil', 'serialwin32', 'sys', 'time', 'to_bytes', 'unicode', 'win32']

pyserial库已正确安装,Python 正在使用正确版本的 serial`模块,模块路径指向标准的 site-packages 目录。

妈的,又不报错了,执行结果如下。

1
---异常---: could not open port 'COM2': FileNotFoundError(2, '系统找不到指定的文件。', None, 2)

先把控件详解和基本指令集看完。

控件详解。

控件详解,挨个把所有控件用途和属性大概过一遍。

Python 通信。

[嵌入式] USART HMI智能串口屏 一篇就够!保姆级教程

虚拟串口驱动详解与应用实践指南-CSDN博客

1
2
3
  File "C:\Users\Lenovo\AppData\Local\Programs\Python\Python314\Lib\site-packages\serial\serialwin32.py", line 64, in open
raise SerialException("could not open port {!r}: {!r}".format(self.portstr, ctypes.WinError()))
serial.serialutil.SerialException: could not open port 'COM9': FileNotFoundError(2, '系统找不到指定的文件。', None, 2)

com0com。

com0com虚拟串口使用教程-CSDN博客

Download Null-modem emulator

image-20251125162340876

安装之后有人会出现带黄色感叹号这种,操作系统设置-高级启动-禁用强制驱动程序签名就可以,或者找其他博主的解决方案,因为我的电脑没出现,所以没办法验证方法的可行性。

image-20251125162418397

虚拟串口com0com安装后设备管理器是感叹号的解决办法_安装虚拟com 黄色感叹号-CSDN博客

直接在设置/Windows更新/高级选项/可选更新下面选择。

image-20251125162845479

1
安装com0com软件后在设备管理器中虚拟串口黄色感叹号

win11在安装com0com软件后,在设备管理器中虚拟串口黄色感叹号得解决方法_com0com 黄色感叹号-CSDN博客

虚拟串口com0com安装后设备管理器是感叹号的解决办法_安装虚拟com 黄色感叹号-CSDN博客

解决了。

下载串口调试工具,测试虚拟串口能不能正常工作,USART HMI 程序尝试联机虚拟串口失败,试下重启软件和重启电脑后管不管用。

应该没什么问题。

1
点击连接本机虚拟串口后,左下角显示尝试联机COM3:2400,这是什么意思,为什么点击联机后软件卡死了呢。
1
USART HMI怎么修改联机波特率。

触摸屏PPI通信如何更改波特率_百度知道

image-20251125173604479

虚拟串口是成对出现的。com0com创建了一对虚拟串口,比如COM3和COM4。这两个端口是相互连接的:发送到COM3的数据会被COM4接收,反之亦然。

所以,正确的做法是:

  1. 将USART HMI程序连接到其中一个虚拟串口(例如COM3)
  2. 将Python程序连接到另一个虚拟串口(例如COM4)

这样,当Python程序向COM4写入数据时,USART HMI程序从COM3读取数据;当USART HMI程序向COM3写入数据时,Python程序从COM4读取数据。

测试成功。

1
2
3
4
5
6
7
8
9
10
11
12
13
import serial #导入模块
try:
ser=serial.Serial(port="COM4",baudrate=115200,timeout=5)
print("串口详情参数:", ser)

#result=ser.write("page page0".encode("utf-8"))
#result=ser.write("page page2".encode("GB2312"))
result=ser.write("t0.txt=\"淘晶驰电子!\"".encode("GB2312"))

ser.write(bytes.fromhex('ff ff ff'))
ser.close()#关闭串口
except Exception as e:
print("---异常---:",e)

模拟器勾选用户MCU输入,自定义波特率 115200 并选择本机虚拟串口号COM3,点击开始监听;本机启动 Python 程序向虚拟串口号COM4发送数据,模拟器响应正常。

今天的工作结束了。

串口助手软件(sscom)和串口屏模拟器联调1 — 淘晶驰串口屏资料中心 1.1.0-2025-11-21 14:36:46 文档

串口助手软件可以下载安装官网推荐的 sscom。

2025 年 11 月 24 日

1
2
1.硬件建设
基于信创技术路线,研发一套适配监测运维系统的专用设备。要求处理器、操作系统都在信创名录,支持4.3 英寸480x1920分辨率的TFT 显示屏,具备常用的IO输入输出功能。

usart hmi(串口屏)介绍-CSDN博客

前言 — 三易串口屏开发指南 1.4.0 文档

淘晶驰资料中心 — 淘晶驰串口屏资料中心 1.1.0-2025-11-21 14:36:46 文档

快速入门 — 淘晶驰串口屏资料中心 1.1.0-2025-11-21 14:36:46 文档

image-20251124095011903

淘晶驰串口屏官网,下载安装 USART HMI 上机位开发软件。

下载安装。

USB 转 TTL 模块还没到,估计这个模块物流到了之后就能正常连接串口屏进行开发了,先看官网学习下怎么开发。

1
2
3
4
总可用内存:3584
全局内存占用:16
错误:页面:page0 失败:t0.font 初始值无效:字库ID无效
错误:编译失败! 1个错误, 0个警告

先解决下这个问题。

工具栏下点击字库制作,解决问题。

image-20251124111137745

到手测试 — 淘晶驰串口屏资料中心 1.1.0-2025-11-21 14:36:46 文档


1
用  USART HMI 软件开发串口屏应用,用到了什么语言?能使用python脚本帮助开发吗

了解USART HMI串口屏应用的开发语言以及Python的辅助开发方式很有用。简单来说,核心的界面设计和逻辑控制使用了一套类似C语言的专用脚本,而Python则可以作为强大的辅助工具,在特定场景下大显身手

下面这个表格能帮你快速了解不同语言在USART HMI开发中的角色。

开发环节/用途 主要使用语言/技术 Python的可参与性
串口屏界面逻辑 USART HMI专用脚本 (类C语言) 无法直接使用。屏幕固件只解析专用指令。
下位机(单片机)程序 C / C++ (如STM32的Arduino、HAL库) 可以。通过PySerial库控制串口与屏幕通信。
上位机(PC)测试工具 通常为C++(Qt)/C#(WinForm)等 非常适合。Python(PyQt/PySerial)可快速搭建调试助手。
嵌入式脚本控制器 通常为C/C++ 可以。在运行MicroPython的ESP32/ESP8266等单片机上,可直接用Python驱动屏幕。

1
那么使用 Pycharm 编辑器能达到跟 USART HMI 一样的开发效果吗?
特性维度 USART HMI 软件 PyCharm
核心定位 串口屏专用集成开发环境 通用Python IDE
界面设计 可视化拖拽,专有控件,所见即所得 需借助 PyQt、Tkinter 等库编写代码来创建图形界面
通信逻辑 内建串口通信协议,自动生成指令帧 需手动调用 pyserial等库编写底层串口通信代码
程序运行 代码最终下载到串口屏硬件中运行 Python 代码在电脑上运行
适用场景 专注于为嵌入式设备开发人机交互界面 通用软件开发,如Web后端、数据分析、AI、脚本工具等
1
实时时钟的缩写是RTC(Real_Time Clock)。RTC 是集成电路,通常称为时钟芯片。
1
实时时钟芯片是日常生活中应用最为广泛的消费类电子产品之一。它为人们提供精确的实时时间,或者为电子系统提供精确的时间基准,实时时钟芯片大多采用精度较高的晶体振荡器作为时钟源。有些时钟芯片为了在主电源掉电时,还可以工作,需要外加电池供电。

配置文件常用的系统变量:

image-20251124113840848

请注意:page是跳转页面的指令,page指令后面的代码将不会执行。

Program.s 配置文件内容:

1
2
3
4
5
6
7
//以下代码只在上电时运行一次,一般用于全局变量定义和上电初始化数据
int sys0=0,sys1=0,sys2=0 //全局变量定义目前仅支持4字节有符号整形(int),不支持其他类型的全局变量声明,如需使用字符串类型可以在页面中使用变量控件来实现
baud=9600//配置波特率
dim=100//配置屏幕背光
recmod=0//串口协议模式:0-字符串指令协议;1-主动解析协议
printh 00 00 00 ff ff ff 88 ff ff ff//输出上电信息到串口
page 0 //上电刷新第0页

下午再研究下代码编写吧,中午吃完饭顺便把串口屏带上,对照着官网引导看看这屏幕的构造啥的。


午饭后。

书写语法。

滑块实现调节屏幕亮度:

1
dim=h0.val

按钮实现跳转至新的页面:

1
dim=h0.val

实现一个定时器:

1
2
错误:全局变量定义必须放在Program.c文件中所有代码之前:int timer_count = 0(双击此处定位代码)
错误:编译失败! 1个错误, 0个警告
1
2
3
4
5
6
7
8
9
10
//以下代码只在上电时运行一次,一般用于全局变量定义和上电初始化数据
int sys0=0,sys1=0,sys2=0 //全局变量定义目前仅支持4字节有符号整形(int),不支持其他类型的全局变量声明,如需使用字符串类型可以在页面中使用变量控件来实现
int timer_count = 0 //定义定时器

baud=9600//配置波特率
dim=100//配置屏幕背光
recmod=0//串口协议模式:0-字符串指令协议;1-主动解析协议

printh 00 00 00 ff ff ff 88 ff ff ff//输出上电信息到串口
page 0 //上电刷新第0页
1
2
3
4
5
timer_count++
if(timer_count==10)
{
page main
}

这个编译器报错后的代码定位功能真不错。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//给n0控件的val属性赋值123
n0.val=1
//把h0控件的val属性赋值给n0控件的val属性
n1.val=h0.val
//给系统变量dim赋值80(背光亮度立即变为80亮度)
dim=90
//给系统变量bauds赋值115200(屏幕波特率立即变为115200)
bauds=115200
//把屏幕当前的波特率系统变量赋值给n0控件的val属性
n2.val=bauds
// 如果有变量需要格式化显示
//t0.txt="数值="+n0.val
//设置p0控件显示id为2的图片
p0.pic=0
//设置p0控件显示id为n0.val的图片
p1.pic=n0.val

运算操作。

1
2
3
所有运算不支持乘除法优先,也不支持括号优先级,统一从左到右的顺序,请特别注意。
所有的运算操作可以在上位编辑状态下写入控件事件中,也可以串口传输过来(串口传输记得加三个0xff的结束符)
所有的运算操作都不支持多余空格,添加进任何空格,编译都会报错

跨页面赋值,全局变量。

多数情况下,我们都是在操作当面页面的控件属性,如果需要操作其他页面的控件属性需要提前将对应的控件设置为全局。

[页面].[控件].[属性]=XXX。

逻辑语句。

1
2
所有的逻辑语句只能在上位编辑状态下写入控件的事件中,不支持串口传输逻辑语句。
所有的判断语句都不支持多余空格,添加进任何空格,编译都会报错
1
2
3
4
5
6
7
if(b0.txt=="开始")
{
b0.txt="停止"
}else
{
b0.txt="开始"
}

n0.val一直自加到100为止,在自加过程中屏幕会一直不断的刷新n0控件的显示。

1
2
3
4
5
while(n0.val<100)
{
n0.val++
doevents
}

串口屏返回数据格式。

取消设备开机固定发送开机信息,转为Program.s中由代码发送开机信息(用户可自行注释或者删除)。

1
2
printh 00 00 00 ff ff ff 88 ff ff ff//输出上电信息到串口
page 0 //上电刷新第0页
1
2
3
4
00 00 00     - 帧头或预留字段(可能是设备地址或命令类型)
ff ff ff - 分隔符(USART HMI协议中常用0xFF作为分隔)
88 - 核心数据:上电状态码(136的十六进制)
ff ff ff - 结束符(USART HMI协议要求每条指令以30xFF结束)

系统变量。

在program.s中只能使用int来定义全局变量,无法定义char,float,double等类型,使用int定义的语句必须写在program.s的最前面

返回上一页。

某些情况下,可能从多个不同的页面跳转到同一个页面,这时如果在“返回”按钮中直接跳转到某一个页面,就会造成逻辑混乱,此时可以使用以下方法解决:

在program.s中新建一个lastpage变量。

1
int lastpage    //定义上一页

在每一个页面的离开事件中添加以下代码,将dp的值赋值给 lastpage 变量。

1
lastpage=dp

添加一个“返回上一页”按钮,在按钮中跳转到 lastpage 所保存的值。

1
page lastpage

刷新当前页面。

1
page dp

volume,体积;卷,合订本;容积,容量;量,额;音量,响度;大量,许多;书;(成套书籍中的)一卷,一册;最大音量;唱片;一套录音磁带。

背光亮度值。

1
2
3
dim和dims区别
dim:不会自动关机保存
dims:会自动保存,下次开机时还是此亮度值

通过滑块设置亮度。

在前初始化事件中将dim赋值给进度条。

1
h0.val=dim

在滑动事件中将进度值赋值给dim。

1
dim=h0.val

在弹起事件中将进度值赋值给dims。

1
dims=h0.val

进度值的最小值建议设置为5,避免完全黑屏后没有办法调回来。

1
min

配置波特率值。

1
baud=9600	//配置波特率

无串口数据自动睡眠时间,无触摸操作自动睡眠时间。

1
2
//ussp=30 //30秒无串口数据自动进入睡眠模式
thsp=10 //10秒无触摸操作自动进入睡眠模式

睡眠。

1
sleep=1

随机数。

1
2
3
4
5
6
7
8
//设置随机数取值范围0-9999
randset 0,9999
n0.val=rand
n1.val=rand
n2.val=rand
n3.val=rand
n4.val=rand
//n0,n1,n2,n3,n4大概率是5个毫不相干的值,当然由于概率的原因可能有重复的

修改颜色。

1
2
3
4
5
6
t0.txt="白色字体黑色背景"
t1.txt="绿色字体红色背景"
t0.pco=WHITE//设置t0字体色为白色
t0.bco=BLACK//设置t0背景色为黑色
t1.pco=GREEN//设置t1字体色为绿色
t1.bco=RED//设置t1背景色为红色

串口屏调试。

淘晶驰串口屏的模拟器功能非常强大,您可以在没有串口屏实物的情况下通过模拟器进行调试,上位机模拟器几乎可以完全模拟真实的设备,在指令输入区可以直接输入指令,不需要结束符。

在指令输入区可输入指令给文本赋值,不需要对引号进行转义。

跳转页面,给文本/数字赋值,设置屏幕背光。

1
2
3
page page1
n0.val=10000
dim=90

也可以修改文本控件的背景颜色,可以通过指令修改的是控件的“绿色属性”。

调整控件大小。

t0.txt=”淘晶驰电子”和t0.bco=YELLOW的区别是一个加了引号,一个没有加引号,这是因为txt是字符串,bco则是整形,目前只txt,path,dir,filter是字符串,其他均为整形。

黑色属性和绿色属性。

如下图所示:属性栏中显示为绿色的属性都可以在运行中修改,黑色的属性只能在编辑时修改,不能在运行中修改。

USB转TTL的接线说明。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
串口屏的TX(发送)接USB转TTL的RX(接收)

串口屏的RX(接收)接USB转TTL的TX(发送)

串口屏的GND要和USB转TTL的GND连接在一起(共地)

如果上电时发现串口屏不断重启(4.3寸以上的屏幕以及带喇叭的屏幕),可能是供电不足,这时候需要并联一个电源来提高电流

需要额外找一个电源引出电源正负极,注意正负极之间的电压应该在屏幕的供电范围内

将电源正极接到屏幕的VCC上(请注意电压范围)

电源负极接到GND(共地)

这样就实现了对屏幕的额外供电

1
单片机,串口屏,这些东西有什么关联呢?

单片机和串口屏的关系就像是 “大脑”“脸” 的关系。

  • 单片机大脑:负责核心的逻辑计算、数据处理、控制决策。
  • 串口屏:负责信息显示和人机交互(触摸),并把用户的指令反馈给大脑。

它们各司其职,共同构成一个完整的嵌入式人机交互系统。下面这个表格清晰地展示了两者的分工与合作。

模块 角色 核心任务 相当于
单片机 大脑/主控 执行核心逻辑、运算、控制外设、与传感器/执行器通信 汽车的发动机、ECU
串口屏 脸/交互界面 显示图形界面、接收触摸指令、将指令转发给单片机 汽车的仪表盘、方向盘、中控屏

这种设计带来的优势:

优势 说明
降低开发难度 单片机程序员无需编写复杂的图形驱动和触摸识别代码,只需通过简单的串口指令即可控制精美界面。
专业分工 硬件工程师专注于单片机功能,UI设计师专注于屏幕界面设计,两者通过串口协议对接,协作高效。
灵活更换 只要通信协议不变,可以轻易更换不同尺寸、不同厂家的串口屏,而无需修改单片机的主程序。

通俗地解释单片机开发:

Web开发 单片机开发 思维转换
处理数据 控制电流 你写的代码会直接影响物理世界的电压和电流。
逻辑错误导致页面报错 逻辑错误可能导致短路、冒烟 对硬件要心存敬畏,连接电路前务必检查!
崩溃了可以刷新页面 代码写死可能导致芯片“变砖” 需要掌握如何通过“烧录器”来救援。

1
串口屏开发需要用到 USART HMI 编辑器,那么单片机开发呢,常用的编辑器有哪些?

什么是单片机?

什么是嵌入式开发?

和USB转TTL的接线说明 — 淘晶驰串口屏资料中心 1.1.0-2025-11-21 14:36:46 文档

和单片机的接线说明 — 淘晶驰串口屏资料中心 1.1.0-2025-11-21 14:36:46 文档

1
2
3
- 模拟器(电脑)与串口屏实物连接
- 模拟器(电脑)与单片机连接
- 串口屏与单片机连接

任意两个设备之间连接,均是TX接对方的RX,RX接对方的TX,GND接对方的GND,如果双方都有独立的供电,可以不接5V线

电脑与串口屏连接。

  1. 必须保证下面选择的是“键盘输入”,上面才能选择模拟器和串口,与串口屏进行联机。
  2. 当模拟器和一个串口屏实物连接成功时,底部将会显示联机成功,以及联机获取到的串口屏参数。
  3. 此时输入指令,指令会被同时发送到模拟器和串口屏实物上
  4. 除此之外,如果屏幕是带RTC的(例如X5/K0系列),可以校准RTC(必须是在联机成功的状态下),校准前请安装串口屏实物背部的RTC电池,型号是CR1220
1
淘晶驰强大的模拟器功能几乎能够实现与实体屏幕几乎一样的功能,除外部IO,电阻屏校准等实物特有的少数功能外,几乎都可以在屏幕上完全仿真。您可以提前通过(电脑上的)模拟器和单片机之间进行通讯,在屏幕样品快递之前就能完成大部分功能的开发和调试,大大降低开发和选型成本。

电脑与单片机连接。

这个连接方式在这里不再详述。

单片机与串口屏连接。

淘晶驰官方的USB转TTL转接板,其作用有以下两种:

1、电脑通过USB转TTL连接串口屏进行下载或者调试。

2、电脑通过USB转TTL连接单片机进行通讯。

如果不使用电脑时,请勿通过USB转TTL工具连接单片机和串口屏,怎么接都不行!

部分客户使用的是正点原子、野火等公司的开发板,看到板子上有USB口,就直接把USB转TTL工具插上去。

或者是开发板有microUSB接口或者typec接口,把自己的手机充电线插进去,这是万万不行的。

因为这时候你的单片机开发板是接入了一个USB设备而不是USART设备,除非你去驱动这个USB外设先与这个USB转TTL进行通讯,否则是不能跟屏幕进行通讯的。

1
USART(Universal Synchronous/Asynchronous Receiver/Transmitter)是一种全双工通用同步/异步串行收发模块,作为高度灵活的串行通信设备,支持相互独立的收发数据操作。其核心功能涵盖同步/异步双模式运行,可配置59位数据位及1-2位停止位,内置硬件奇偶校验、数据溢出检测、帧错误检测等机制,并具备三级中断支持。
1
模块结构包含时钟发生器、数据发送器与接收器三大组件,其中时钟发生器由同步逻辑电路和波特率发生器组成,发送器采用缓冲器实现连续传输,接收器则集成两级缓冲器和噪声滤波器。USART与UART保持寄存器位定义、波特率生成等基础兼容性,同时在接收缓冲器设计和错误标志处理方面有所改进。

串口屏与单片机连接 — 淘晶驰串口屏资料中心 1.1.0-2025-11-21 14:36:46 文档

所有单片机与串口屏连接的注意事项,直接点击上文的官网链接查看即可。

串口屏通信协议。

串口屏通讯协议 — 淘晶驰串口屏资料中心 1.1.0-2025-11-21 14:36:46 文档

这方面稍后再做了解。

其他联调方案暂且放过,重点可以看看与 Python 之间的联调。

与python联调 — 淘晶驰串口屏资料中心 1.1.0-2025-11-21 14:36:46 文档

python发送数据给屏幕 — 淘晶驰串口屏资料中心 1.1.0-2025-11-21 14:36:46 文档

2025 年 11 月 18 日

(99+ 封私信 / 82 条消息) 「笔记分享」迪文串口屏基础GUI开发流程 - 知乎

串口屏介绍及开发环境搭建-CSDN博客

2025 年 11 月 17 日

什么是串口屏?串口屏该怎么选?

串口屏介绍及开发环境搭建-CSDN博客

串口屏入门-CSDN博客

迪文串口屏例程-CSDN博客

[ Arduino+Python ] 做一个串口屏,显示CPU使用率_python做硬件显示屏-CSDN博客

Python串口调试-CSDN博客

Python 读取串口并在终端显示,界面虽然不好看,但是有时候可以用来调试程序。

右键菜单

2025 年 11 月 20 日

更新下注册表中 command 的参数配置

1
"D:\dev\Visual Studio Code\Microsoft VS Code\Code.exe" "%1"

在注册表命令中添加正确的参数后,成功让 VSCode 基于选定的文件夹打开,其他所有程序的参数配置类似。

2025 年 11 月 18 日

image-20251118100536190

  1. 在以下关键路径下逐一查找路径1:文件/文件夹通用菜单计算机\HKEY_CLASSES_ROOT*\shell``计算机\HKEY_CLASSES_ROOT*\shellex\ContextMenuHandlers路径2:文件夹背景菜单(最可能的位置)计算机\HKEY_CLASSES_ROOT\Directory\Background\shell``计算机\HKEY_CLASSES_ROOT\Directory\Background\shellex\ContextMenuHandlers路径3:驱动器菜单计算机\HKEY_CLASSES_ROOT\Drive\shell``计算机\HKEY_CLASSES_ROOT\Drive\shellex\ContextMenuHandlers
  2. 搜索方法:逐个展开上述路径。例如,展开 Directory\Background\shell,您会看到很多子项。

搜狗的 彻底粉碎文件清理大文件/重复文件 两个篡改右键菜单项,我在这两个目录下成功删除了:

1
计算机\HKEY_CLASSES_ROOT\Directory\Background\shellex\ContextMenuHandlers
1
计算机\HKEY_CLASSES_ROOT\Directory\shellex\ContextMenuHandlers

夸克云盘也一样。


1
注册表里有这些数据是什么意思啊:{A470F8CF-A1E8-4f65-8335-227475AA5C46}
  1. 这个 GUID 在哪里?

根据我们之前的对话上下文,您很可能是在以下路径中找到它的:

1
HKEY_CLASSES_ROOT\Directory\Background\shellex\ContextMenuHandlers

在这个路径下,您会看到一个或多个以 GUID 命名的子项。您截图中编辑的,正是某个此类子项下的 (默认)值。

  1. 这个结构是什么意思?

这种结构代表右键菜单的一种注册方式

  • 方式A:简单菜单(我们之前讨论的)路径:...\shell\某个名称特点:直接在 (默认)值里填写要显示的文字(如“上传到夸克网盘”),并在其下建立 command子项来指定要运行的程序。这种是直接、简单的命令。
  • 方式B:复杂菜单(您截图中的方式)路径:...\shellex\ContextMenuHandlers\某个GUID特点:(默认)值里不填菜单文字,而是填一个 GUID。这种是告诉系统:“去调用这个编号的组件来生成菜单”。优点:功能强大,可以实现动态菜单(如“使用Notepad++打开”的子菜单)、图标、条件判断等复杂逻辑。
  1. 这个特定的 GUID 是什么?

您截图中的 GUID {85212cfd-77ed-4add-8e24-a0a39e3dbfc3}经过查询,是 Microsoft 自家组件 的标识符,通常与 “设置”或“个性化” 等相关。它很可能是系统自带的某个右键菜单项(例如在新版Windows中,桌面右键菜单里的“显示设置”或“个性化”)。

这很重要,因为它意味着这个菜单项大概率是系统核心功能,而不是您想删除的第三方软件(如搜狗捆绑的软件)添加的。


1
YunShellExt

在这个目录下有这个注册表文件:

1
计算机\HKEY_CLASSES_ROOT\Directory\shell\YunShellExplorerCommand
1
计算机\HKEY_CLASSES_ROOT\AllFilesystemObjects\shellex\ContextMenuHandlers\SendTo
1
计算机\HKEY_CLASSES_ROOT\*\shellex\ContextMenuHandlers\YunShellExt

依次删除以上目录,我把第三条注册表目录删除以后,右键点击文件后百度网盘的菜单项竟然消失了。

1
计算机\HKEY_CLASSES_ROOT\*\shell\YunShellExplorerCommand
1
计算机\HKEY_CLASSES_ROOT\Directory\shellex\ContextMenuHandlers\YunShellExt

依次删除以上目录,我把第而二条注册表目录删除以后,右键点击文件夹后百度网盘的菜单项也消失了。

清爽。

windows10系统下以管理员身份进入CMD(命令提示符)的三种方法_管理员身份win 10进入命令方法-CSDN博客

  1. 搜索栏直接搜索:点击任务栏上的「搜索框」(Win10 在左侧,Win11 在中间),输入 “cmd” 或 “命令提示符”,搜索结果中会显示 “命令提示符”,点击即可打开(若需管理员权限,右键选择 “以管理员身份运行”);
  2. 开始菜单查找:点击桌面左下角「开始菜单」(Win 图标),Win10 在 “Windows 系统” 文件夹中找到 “命令提示符”,Win11 需展开 “所有应用”,在字母 “W” 分类下的 “Windows 工具” 里找到 “命令提示符”,点击打开;
  3. 快捷键 “Win+R” 调用:按下键盘上的 “Win 键 + R 键”(Win 键即带 Windows 图标的按键),弹出 “运行” 对话框,输入 “cmd”,点击 “确定” 或按回车,即可快速打开命令提示符(若需管理员权限,在 “运行” 对话框中输入 “cmd” 后,按住 “Ctrl+Shift + 回车”)。

2025 年 11 月 17 日

自今年夏天七月初格式化C盘之后,之前自定义过的开始菜单磁铁、系统环境变量以及右键菜单都被重置了,给日常使用添了不少麻烦。

今早花时间搞搞这个。

夸克网盘,右键文件夹篡改的注册表目录在这里:

1
计算机\HKEY_CLASSES_ROOT\Directory\shell

这是任意空白处点击右键的注册表目录:

1
计算机\HKEY_CLASSES_ROOT\Directory\Background

针对所有文件夹的:计算机\HKEY_CLASSES_ROOT\Directory\shell

针对空白处的:计算机\HKEY_CLASSES_ROOT\Directory\Background\shell

针对所有文件(包括文件夹)的:计算机\HKEY_CLASSES_ROOT\AllFilesystemObjects\shell

以上是系统用户的,部分配置也可能在当前用户下:计算机\HKEY_CURRENT_USER\Software\Classes\Directory\shell

百度网盘,夸克云盘,搜狗,这三个注册表菜单都是自定义的吗,放在计算机\HKEY_CLASSES_ROOT目录下的话就不好找了。

还是先考虑添加任意空白处的注册表策略吧。

主要是常用的编译器。

PING

2025 年 11 月 17 日

1
"C:\Windows\System32\PING.EXE"

系统环境变量里新增如下一条即可:

1
C:\Windows\System32\

云盘

2025 年 11 月 17 日

查看一下D盘磁盘空间,妈的都爆红了,D盘磁盘空间一共275GB现在就剩19G多了,不知道给我电脑里装了些什么玩意儿。

sofware 占用空间大小17GB,blog 占用空间大小3.5GB,dev 占用空间大小70GB,project 占用空间55GB,program 占用空间ji5GB。

四个主要文件夹一共占用空间150.5GB

隐藏文件夹里,视频占用空间14.6GB,图片占用空间32.3GB,文档占用空间29GB,桌面将近2GB。

这些都是占大头的,一共占用空间77.9GB

CI/CD

2025 年 11 月 13 日

我一个写Java的,怎么就开始玩K8s和Jenkins了?!前几天接到一个新任务,要求把以前部署在私有服务器上的项目,全 - 掘金

Jasypt

2026 年 1 月 16 日

[springboot 之 使用jasypt加密解密插件通俗易懂]-腾讯云开发者社区-腾讯云

用同一个密码加密同样的内容,每次生成的密文都不一样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
String password = "mySecretKey";
String text = "123456";

// 第一次加密
String encrypted1 = encryptor.encrypt(text); // 结果可能是: ABC123DEF456
// 第二次加密(同样的密码,同样的明文)
String encrypted2 = encryptor.encrypt(text); // 结果可能是: XYZ789UVW012
// 第三次加密
String encrypted3 = encryptor.encrypt(text); // 结果可能是: LMN345OPQ678

// 但神奇的是,它们都能正确解密回 "123456"
System.out.println(encryptor.decrypt(encrypted1)); // 输出: 123456
System.out.println(encryptor.decrypt(encrypted2)); // 输出: 123456
System.out.println(encryptor.decrypt(encrypted3)); // 输出: 123456

为什么能这样?

Jasypt 使用了 盐(Salt) 技术:

  1. 每次加密时,会生成一个随机的盐值
  2. 盐值和密文一起存储
  3. 解密时,从密文中提取盐值,然后用相同的盐值解密

加密过程:

1
明文 "123456" + 随机盐值 → 加密 → 密文(包含盐值)

解密过程:

1
密文(包含盐值) → 提取盐值 → 解密 → 明文 "123456"

2025 年 11 月 12 日

JAVA集成Jasypt进行加密、解密(SpringBoot)_jasypt在线加密解密工具-CSDN博客

集成步骤总结:

引入 Jasypt 依赖。

1
2
3
4
5
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>3.0.4</version> <!-- 请根据需要更新到最新版本 -->
</dependency>

配置加密密钥。

1
2
3
jasypt:
encryptor:
password: mysecretkey

对敏感数据进行加密,并在配置文件中使用 ENC() 包裹加密值。

使用 Jasypt 命令行工具加密,Jasypt 提供了命令行工具来加密数据,执行以下命令加密数据库密码:

1
java -cp jasypt-1.9.3.jar org.jasypt.intf.cli.JasyptPBEStringEncryptionCLI input="your_password" password="mysecretkey" algorithm=PBEWithMD5AndDES

其中:

  1. input 是要加密的文本(例如数据库密码)
  2. password 是加密密钥(与我们在配置文件中设置的 mysecretkey 一致)
  3. algorithm 是加密算法,这里使用 PBEWithMD5AndDES。

执行后,将得到类似以下的加密文本输出:

1
5MG1sSxAx+EeJsBdmcVdKg==

将加密后的文件写入配置文件:

1
2
3
spring:
datasource:
password: ENC(5MG1sSxAx+EeJsBdmcVdKg==)

当 Spring Boot 项目启动时,Jasypt 会自动识别 ENC() 包裹的加密内容,并使用密钥进行解密,无需额外的代码逻辑。这意味着在运行时,应用能够使用解密后的值。

可根据需求自定义加密器,选择适合的加密算法和配置。

分支

2025 年 11 月 11 日

1
Merge incoming changes into the current branch
1
Rebase the current branch on top of incoming changes

在更新分支时,选择“Merge incoming changes”还是“Rebase the current branch”会带来截然不同的项目历史记录。为了帮你快速把握核心区别,下面这个表格汇总了它们的主要特性。

特性 Merge (合并) Rebase (变基)
历史记录结构 保留分叉与合并点,形成图状结构 生成线性历史记录,隐藏分叉
操作原理 创建一个新的合并提交,将两个分支的历史联系在一起 将当前分支的提交“移动”到目标分支的最新提交之后
提交哈希值 原有提交的哈希值不变 被移动的提交会生成全新的哈希值
适用分支 公共分支(如main、develop) 本地分支或未推送的分支
冲突处理 在合并提交时一次性解决所有冲突 在“移动”每个提交时都可能遇到冲突,需逐个解决

用 Rebase 来清理本地历史,用 Merge 来将工作成果集成到公共代码库。

安装部署

2025 年 11 月 10 日

下午锦浩给我拿U盘提供了几份安装部署文档。

image-20251110174823921

DBeaver(数据库管理工具),FileZilla(FTP/SFTP文件传输工具), MobaXterm(全能终端工具), Notepad++(代码编辑器),oCam(屏幕录制软件),Postman(API测试工具),Xshell(SSH终端客户端)。

1
公司加班是有补贴的,平时20/小时,周末30元/小时,下班了可以在公司继续学习,提升专业能力,进而涨工资,这才是提高收入的正道。

JDK 安装配置。

1
2
3
4
[admin@localhost ~]$ java -version
openjdk version "1.8.0_131"
OpenJDK Runtime Environment (build 1.8.0_131-b12)
OpenJDK 64-Bit Server VM (build 25.131-b12, mixed mode)

Docker 安装配置。

查看 Docker 版本。

1
docker --version

查看 CentOS 虚拟机版本,经尝试后执行以下五个命令任意一个都可以。

1
cat /etc/centos-release
1
2
[admin@localhost ~]$ cat /etc/centos-release
CentOS Linux release 7.4.1708 (Core)
1
cat /etc/os-release
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[admin@localhost ~]$ cat /etc/os-release
NAME="CentOS Linux"
VERSION="7 (Core)"
ID="centos"
ID_LIKE="rhel fedora"
VERSION_ID="7"
PRETTY_NAME="CentOS Linux 7 (Core)"
ANSI_COLOR="0;31"
CPE_NAME="cpe:/o:centos:centos:7"
HOME_URL="https://www.centos.org/"
BUG_REPORT_URL="https://bugs.centos.org/"

CENTOS_MANTISBT_PROJECT="CentOS-7"
CENTOS_MANTISBT_PROJECT_VERSION="7"
REDHAT_SUPPORT_PRODUCT="centos"
REDHAT_SUPPORT_PRODUCT_VERSION="7"
1
rpm -q centos-release
1
centos-release-7-4.1708.el7.centos.x86_64
1
uname -r
1
2
[admin@localhost ~]$ uname -r
3.10.0-693.el7.x86_64
1
cat /etc/redhat-release
1
2
[admin@localhost ~]$ cat /etc/redhat-release
CentOS Linux release 7.4.1708 (Core)

image-20251110182901580

1
arm64文件夹里也有个 docker 安装包,docker-24.0.6.tgz,这与 docker-20.10.24.tgz 有什么区别呢,我的 CentOS7 应该安装哪个?

对于 CentOS 7 系统,您应该安装 docker-20.10.24.tgz这个版本

不建议尝试安装 arm64文件夹下的 docker-24.0.6.tgz,主要原因有两个,其中第一个是致命错误:

  1. 架构不兼容(最关键原因)arm64文件夹下的安装包是专门为 ARM 架构的CPU(例如苹果M系列芯片、某些国产化平台、树莓派等)编译的。而您的CentOS 7系统几乎可以肯定是在 x86_64(即AMD64)架构的服务器上运行的。强行安装会导致程序根本无法执行。
  2. 版本兼容性:即使架构匹配,Docker 24.x 这样的新版本对旧操作系统的支持也可能不佳,容易遇到依赖库版本等问题。

Linux安装Docker详细教程! 本文已参与「新人创作礼」活动,一起开启掘金创作之路。 前置条件 前置知识 1、 - 掘金

Docker 万字入门教程Docker 使用 Google 公司推出的 Go 语言进行开发实现,对进程进行封装隔离,属于 - 掘金

Centos安装Docker的详细安装步骤,Docker相关组件:docker-ce-cli、docker-ce和containerd.io的区别 - 掘金

1、使用官方便捷脚本安装(最快速,适用于测试)

1
2
3
4
5
6
7
8
9
# 下载安装脚本
curl -fsSL https://get.docker.com -o get-docker.sh

# 运行脚本(使用root权限)
sudo sh get-docker.sh

# 或者,如果希望安装特定版本而不是最新版,可以设置版本变量
# export VERSION=20.10.24
# sudo sh get-docker.sh

2、配置 yum 源在线安装(最有网络环境下的首选),详见卓越之旅栏目下的详细安装过程。

3、使用现有的 .tgz压缩包安装(离线/内网环境首选)

将 docker 安装包 docker-20.10.24.tgz 上传到服务器上

解压,执行以下命令:

1
tar -zxvf docker-20.10.24.tgz

复制,执行以下命令:

1
cp docker/* /usr/bin/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[admin@localhost ~]$ cp docker/* /usr/bin/
cp: 无法创建普通文件"/usr/bin/containerd": 权限不够
cp: 无法创建普通文件"/usr/bin/containerd-shim": 权限不够
cp: 无法创建普通文件"/usr/bin/containerd-shim-runc-v2": 权限不够
cp: 无法创建普通文件"/usr/bin/ctr": 权限不够
cp: 无法创建普通文件"/usr/bin/docker": 权限不够
cp: 无法创建普通文件"/usr/bin/dockerd": 权限不够
cp: 无法创建普通文件"/usr/bin/docker-init": 权限不够
cp: 无法创建普通文件"/usr/bin/docker-proxy": 权限不够
cp: 无法创建普通文件"/usr/bin/runc": 权限不够
[admin@localhost ~]$ sudo cp docker/* /usr/bin/

我们信任您已经从系统管理员那里了解了日常注意事项。
总结起来无外乎这三点:

#1) 尊重别人的隐私。
#2) 输入前要先考虑(后果和风险)。
#3) 权力越大,责任越大。

[sudo] admin 的密码:
admin 不在 sudoers 文件中。此事将被报告。

切换到 root 用户即可避免。

1
2
su -
# 输入root用户的密码

上传 docker.service,上传 docker.service 到 /etc/systemd/system。

1
cp docker.service /etc/systemd/system

授权,执行以下命令:

1
chmod +x /etc/systemd/system/docker.service

重新加载,执行以下命令:

1
systemctl daemon-reload

配置开机启动,执行以下命令:

1
systemctl enable docker
1
2
[root@localhost system]# systemctl enable docker
Created symlink from /etc/systemd/system/multi-user.target.wants/docker.service to /etc/systemd/system/docker.service.

启动,执行以下命令:

1
systemctl start docker

安装成功,查看 Docker 版本。

1
docker version
1
docker --version
1
2
[root@localhost system]# docker --version
Docker version 20.10.24, build 297e128

MySQL 安装配置。

SELinux 关闭,查看SELinux 是否开启:

1
getenforce

关闭当前会话的 SELinux ,重启之后无效:

1
setenforce 0

永久关闭 SELinux ,需要重启:

1
sed -i 's/enforcing/disabled/' /etc/selinux/config

上传安装包,将 mysql 安装包 mysql-8.0.30.tar 上传到服务器上。

加载 mysql 镜像。

1
docker load <mysql-8.0.30.tar
1
2
3
4
5
6
7
8
9
10
11
12
13
[root@localhost admin]# docker load <mysql-8.0.30.tar
553b196ea5e8: Loading layer [==================================================>] 106.8MB/106.8MB
7b1b5089c4c8: Loading layer [==================================================>] 11.26kB/11.26kB
3bf157422b00: Loading layer [==================================================>] 2.293MB/2.293MB
dc4bc61c37e8: Loading layer [==================================================>] 13.9MB/13.9MB
3db93db6878c: Loading layer [==================================================>] 7.168kB/7.168kB
91679720f8d1: Loading layer [==================================================>] 3.072kB/3.072kB
7cd76e295a75: Loading layer [==================================================>] 154.4MB/154.4MB
feee68c33bd4: Loading layer [==================================================>] 3.072kB/3.072kB
43c8ca9cd771: Loading layer [==================================================>] 183MB/183MB
b670df5ea093: Loading layer [==================================================>] 17.41kB/17.41kB
e24224578084: Loading layer [==================================================>] 1.536kB/1.536kB
Loaded image: mysql:8.0.30

Shell 重定向机制:

1
2
3
4
5
# 实际上发生了这些步骤:
# 1. Shell 打开 mysql-8.0.30.tar 文件
# 2. Shell 将文件描述符重定向到标准输入(stdin)
# 3. docker load 命令从标准输入读取数据
# 4. Docker 解析镜像数据并存储到本地仓库

创建本地数据和配置目录。

1
2
# 创建目录
mkdir -p /data/mysql/data /data/mysql/logs /data/mysql/conf
1
2
# 修改所有者为 UID 999(对应容器内的 mysql 用户)
chown -R 999:999 /data/mysql/data /data/mysql/logs
1
2
# 赋予读写权限
chmod -R 755 /data/mysql/data /data/mysql/logs

创建MySQL容器需要的三个核心目录,并设置用户所有权以及目录权限。

  • /data/mysql/data数据库文件目录(表数据、索引等)
  • /data/mysql/logs日志文件目录(错误日志、慢查询日志等)
  • /data/mysql/conf配置文件目录(my.cnf等)
1
2
3
4
5
6
7
8
9
10
/data/mysql/
├── data/ # 数据库文件(需要mysql用户写权限)
│ ├── ibdata1 # 系统表空间
│ ├── ib_logfile0 # 重做日志
│ └── database/ # 各个数据库
├── logs/ # 日志文件(需要mysql用户写权限)
│ ├── error.log # 错误日志
│ └── slow.log # 慢查询日志
└── conf/ # 配置文件(只需读权限)
└── my.cnf # MySQL配置

创建 MySQL 配置文件。

1
cat > /data/mysql/conf/my.cnf <<EOF

创建文件并写入内容,用于多行文本输入,文件将保存在:/data/mysql/conf/my.cnf

1
2
3
4
5
6
7
8
9
10
11
12
13
[mysqld]
port=3306
datadir=/var/lib/mysql
socket=/tmp/mysql.sock
pid-file=/var/run/mysqld/mysqld.pid
character-set-server=utf8mb4
symbolic-links=0
user=mysql
lower-case-table-names=1
log-error=/var/log/mysql/error.log
sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION'
default_authentication_plugin=mysql_native_password
EOF
1
2
3
4
5
6
7
8
9
10
11
12
13
[mysqld]
port=3306 # MySQL服务端口
datadir=/var/lib/mysql # 数据文件存储路径(容器内路径)
socket=/tmp/mysql.sock # Unix Socket文件路径
pid-file=/var/run/mysqld/mysqld.pid # 进程ID文件路径
user=mysql # 运行MySQL服务的系统用户
symbolic-links=0 # 禁用符号链接(安全加固)
character-set-server=utf8mb4 # 设置默认字符集为UTF8MB4(支持emoji等)
lower-case-table-names=1 # 表名不区分大小写
log-error=/var/log/mysql/error.log # 错误日志路径
sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION' # SQL模式配置
default_authentication_plugin=mysql_native_password # 兼容性考虑:确保老版本MySQL客户端可以连接
EOF

启动容器(注意修改密码)。

1
2
3
4
5
6
7
8
9
10
docker run -d \
--name mysql-server \
-p 3306:3306 \
-v /data/mysql/data:/var/lib/mysql \
-v /data/mysql/logs:/var/log/mysql \
-v /data/mysql/conf/my.cnf:/etc/mysql/my.cnf:ro \
-e MYSQL_ROOT_PASSWORD=Mysql@89s# \
-e TZ=Asia/Shanghai \
--restart=unless-stopped \
mysql:8.0.30
1
2
3
4
5
6
7
8
9
10
docker run -d \                    # 后台运行
--name mysql-server \ # 容器名称
-p 3306:3306 \ # 端口映射
-v /data/mysql/data:/var/lib/mysql \ # 数据持久化
-v /data/mysql/logs:/var/log/mysql \ # 日志持久化
-v /data/mysql/conf/my.cnf:/etc/mysql/my.cnf:ro \ # 配置挂载
-e MYSQL_ROOT_PASSWORD=Mysql@89s# \ # root密码
-e TZ=Asia/Shanghai \ # 时区设置
--restart=unless-stopped \ # 重启策略
mysql:8.0.30 # 镜像版本
1
2
3
4
5
6
7
8
9
10
11
[root@localhost admin]# docker run -d \
> --name mysql-server \
> -p 3306:3306 \
> -v /data/mysql/data:/var/lib/mysql \
> -v /data/mysql/logs:/var/log/mysql \
> -v /data/mysql/conf/my.cnf:/etc/mysql/my.cnf:ro \
> -e MYSQL_ROOT_PASSWORD=Mysql@89s# \
> -e TZ=Asia/Shanghai \
> --restart=unless-stopped \
> mysql:8.0.30
9bc7fd9dd0c7a1f0fabf6d8b3cf712aef5a6ed89d6a19180a6be163ad30c067f

Redis 安装配置。

上传安装包,将安装包 redis-4.0.11.tar 上传到服务器上。

加载 redis 镜像。

1
docker load <redis-4.0.11.tar

创建配置目录和数据目录。

1
mkdir -p /data/redis/conf /data/redis/data

创建配置文件并设置密码(注意修改密码)。

1
cat > /data/redis/conf/redis.conf <<EOF
1
2
3
4
5
requirepass Redis@89s#
dir /data
port 6379
bind 0.0.0.0
EOF

启动 Redis 4.0.11 容器。

1
2
3
4
5
6
7
8
docker run -d \
--name redis-server \
-p 6379:6379 \
-v /data/redis/conf/redis.conf:/etc/redis.conf \
-v /data/redis/data:/data \
--restart=always \
redis:4.0.11 \
redis-server /etc/redis.conf

Nginx 安装配置。

使用nginx -v命令可以直接显示Nginx的版本信息。

使用nginx -V命令可以显示更详细的版本信息,包括编译选项和模块信息。

上传安装包,将 Nginx 安装包 nginx-1.24.0.tar 上传到服务器上。

加载 nginx 镜像,执行以下命令,这个命令用于将一个名为 nginx-1.24.0.tar的归档文件中的 Docker 镜像加载到您的本地 Docker 环境中,使其可以用于创建和运行容器。

1
docker load <nginx-1.24.0.tar
1
2
3
4
5
6
7
8
[root@localhost admin]# docker load <nginx-1.24.0.tar
420179ad2efa: Loading layer [==================================================>] 84.18MB/84.18MB
13c8460bfc9a: Loading layer [==================================================>] 62.52MB/62.52MB
b9a187a24e19: Loading layer [==================================================>] 3.584kB/3.584kB
96c08fed6a4c: Loading layer [==================================================>] 4.608kB/4.608kB
29492f82bbc2: Loading layer [==================================================>] 3.584kB/3.584kB
bc4a3582faa9: Loading layer [==================================================>] 7.168kB/7.168kB
Loaded image: nginx:1.24.0

启动 Nginx 容器(这里先启动复制文件到宿主机)

1
docker run --name nginx -d nginx:1.24.0
1
2
[root@localhost admin]# docker run --name nginx -d nginx:1.24.0
2e536e8ba09f054a20e558c0f34a21575ff00a15ec544b3e0ab53562ac517c3a

创建目的挂载路径

1
mkdir -p /data/nginx/conf /data/nginx/log /data/nginx/html
1
2
3
4
5
/data/
└── nginx/
├── conf/ # 配置文件目录
├── log/ # 日志文件目录
└── html/ # 网站文件目录

复制容器

1
2
# 1. 复制主配置文件
docker cp nginx:/etc/nginx/nginx.conf /data/nginx/conf/nginx.conf
1
2
# 2. 复制配置片段目录
docker cp nginx:/etc/nginx/conf.d /data/nginx/conf/conf.d
1
2
# 3. 复制默认网页文件
docker cp nginx:/usr/share/nginx/html /data/nginx

删除临时容器

1
docker rm -f nginx
1
2
[root@localhost admin]# docker rm -f nginx
nginx

这个临时容器是我们之前为了获取Nginx默认配置文件而故意创建的。如果直接运行并挂载空目录,容器内的 /etc/nginx目录会被宿主机空目录覆盖,Nginx因缺少配置文件而启动失败

这个”临时容器”是有意创建且短期存在的,为了获取Nginx默认配置,完成配置复制后就可以删除。它的配置已经保存到宿主机,所以现在要删除它,然后用持久化的配置重新创建一个”正式容器”。

启动正式容器

1
2
3
4
5
6
7
8
9
docker run \
-p 80:80 \
-p 10001:10001 \
--name nginx \
-v /data/nginx/conf/nginx.conf:/etc/nginx/nginx.conf \
-v /data/nginx/conf/conf.d:/etc/nginx/conf.d \
-v /data/nginx/log:/var/log/nginx \
-v /data/nginx/html:/usr/share/nginx/html \
-d nginx:1.24.0
1
2
3
4
5
6
7
8
9
docker run \
-p 80:80 \ # 端口映射:HTTP
-p 10001:10001 \ # 端口映射:自定义端口
--name nginx \ # 容器命名
-v /data/nginx/conf/nginx.conf:/etc/nginx/nginx.conf \ # 挂载主配置
-v /data/nginx/conf/conf.d:/etc/nginx/conf.d \ # 挂载站点配置
-v /data/nginx/log:/var/log/nginx \ # 挂载日志目录
-v /data/nginx/html:/usr/share/nginx/html \ # 挂载网站文件
-d nginx:1.24.0 # 后台运行
1
2
3
4
5
6
7
8
9
10
[root@localhost admin]# docker run \
> -p 80:80 \
> -p 10001:10001 \
> --name nginx \
> -v /data/nginx/conf/nginx.conf:/etc/nginx/nginx.conf \
> -v /data/nginx/conf/conf.d:/etc/nginx/conf.d \
> -v /data/nginx/log:/var/log/nginx \
> -v /data/nginx/html:/usr/share/nginx/html \
> -d nginx:1.24.0
0a4ea3bae5b05b007da2ece6ca6f0d03e46d154c0e7356ea32fe8353c40704a0

挂载,双向同步。

SELinux 配置。

1
SELinux 是一个内置于 Linux 内核的安全子系统,提供更精细的访问控制,以增强系统安全性。
1
getenforce

getenforce 这个命令用于快速查看 SELinux 的当前运行模式,通常会返回三种结果之一:

  • Enforcing:表示强制模式。SELinux 正在运行,并且会强制执行安全策略,阻止任何未经授权的访问。
  • Permissive:表示宽容模式。SELinux 会记录安全违规行为,但不会实际阻止这些操作。这个模式常用于故障排除和策略调试。
  • Disabled:表示已关闭。SELinux 已被完全禁用
1
setenforce 0

setenforce 0 这个命令能将 SELinux 从 Enforcing(强制模式)立即切换Permissive(宽容模式),而无需重启系统

永久关闭 SELinux ,需要重启后生效:

1
sed -i 's/enforcing/disabled/' /etc/selinux/config

防火墙设置

防火墙查看开放的端口。

1
firewall-cmd --list-ports

防火墙添加3306端口。

1
sudo firewall-cmd --permanent --add-port=3306/tcp
1
sudo firewall-cmd --permanent --add-port=6379/tcp
1
sudo firewall-cmd --permanent --add-port=80/tcp
1
sudo firewall-cmd --permanent --add-port=10051/tcp
1
sudo firewall-cmd --permanent --add-port=10052/tcp
1
sudo firewall-cmd --permanent --add-port=8080/tcp

防火墙重新加载。

1
sudo firewall-cmd --reload

防火墙状态。

1
sudo firewall-cmd --state

2025 年 11 月 11 日

Zabbix-Web 服务启动后果真有问题。

检查容器是否真正启动成功。

1
2
# 查看容器日志(最后20行)
docker logs --tail 20 zabbix-web-nginx-mysql
1
2
3
4
5
6
7
8
9
[root@localhost admin]# docker logs --tail 20 zabbix-web-nginx-mysql
**** MySQL server is not available. Waiting 5 seconds...
**** MySQL server is not available. Waiting 5 seconds...
**** MySQL server is not available. Waiting 5 seconds...
**** MySQL server is not available. Waiting 5 seconds...
**** MySQL server is not available. Waiting 5 seconds...
**** MySQL server is not available. Waiting 5 seconds...
**** MySQL server is not available. Waiting 5 seconds...
**** MySQL server is not available. Waiting 5 seconds...

检查网络连通性。

1
2
3
# 从 Web 容器测试数据库连接
docker exec zabbix-web-nginx-mysql ping 192.168.10.195
docker exec zabbix-web-nginx-mysql nc -zv 192.168.10.195 3306

先把 Zabbix 的问题放一放。

运行监测平台

监测平台服务端。

连接已经启动的 MySQL 服务。

1
2
192.168.139.128
Mysql@89s#

2025 年 11 月 13 日

创建数据库

1
CREATE DATABASE IF NOT EXISTS sx_aiops CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

导入外部数据, sx_aiops_original.sql

执行成功。

上传后端应用包 tomm-rmgt-server.jar 和启动文件 sxrmgt.sh 到部署目录。在部署目录执行./sxrmgt.sh start 启动、./sxrmgt.sh stop关闭、./sxrmgt.shrestart重启。

1
mkdir /opt/tomm_server
1
cp /home/admin tomm-aiops-server.jar /opt/tomm_server
1
chmod  777 /home/admin/sxrmgt.sh 
1
cp /home/admin sxrmgt.sh /opt/tomm_server

后端应用端口号为 8880。

1
./sxrmgt.sh start

登录测试

默认账号密码:

1
2
tail -f nohup.outadmin 
yxjcgly202407!!AA
1
2
3
4
5
6
7
8
[root@localhost tomm_server]# ./sxrmgt.sh start
./sxrmgt.sh: line 1: $'\r': command not found
./sxrmgt.sh: line 3: $'\r': command not found
./sxrmgt.sh: line 10: $'\r': command not found
: No such file or directoryopt/tomm_server
./sxrmgt.sh: line 12: $'\r': command not found
./sxrmgt.sh: line 13: syntax error near unexpected token `$'in\r''
'/sxrmgt.sh: line 13: `case "$1" in

这个错误是因为脚本文件包含了 Windows 的换行符(\r\n),而在 Linux 环境下无法识别。需要将文件格式转换为 Unix 格式。

转换文件格式

1
sed -i 's/\r$//' sxrmgt.sh

检查脚本语法

1
bash -n sxrmgt.sh

给执行权限

1
chmod +x sxrmgt.sh

执行脚本

1
./sxrmgt.sh start
1
2
3
[root@localhost tomm_server]# ./sxrmgt.sh start
=== start tomm-aiops-server
[root@localhost tomm_server]# nohup: appending output to ‘nohup.out’

查看是否启动。

1
netstat -tuln | grep 8880

有显示信息则启动成功。

妈的,文档里是在部署智能运维系统,重新打包一份运行监测 jar 包部署下试试。

1
cp /home/admin tomm-mgt-server.jar /opt/tomm_server
1
./sxrmgt.sh start
1
tail -f nohup.out

image-20251113105246979

启动项目总是显示数据库连接失败,那就把配置文件里数据库配置信息修改成本机虚拟机 ip 地址,同时连接 Docker 容器里启动的 MySQL 服务,待成功执行 SQL 脚本填充库表数据后,再尝试部署项目。

1
D:\Project\luofeng\tomm-mgt-server\script\sql\mysql
1
url: jdbc:mysql://192.168.139.128:3306/sx_monitor?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&nullCatalogMeansCurrent=true&allowMultiQueries=true
1
host: 192.168.139.128

徒劳了,该监测平台连接本地 MySQL 连用户名密码都不一致,这样要改动的配置就更多了,等到正式开发的时候问问就搞定的事情。

不考虑本机部署监测平台项目了,接下来的时间就用来研究 Zabbix 和具体的业务逻辑。

安装部署,到此结束。

Zabbix

2025 年 11 月 13 日

分布式监控平台——Zabbix监控 一、监控的功能 在需要的时刻,提前预警即将出问题,避免故障发生 实时监控系统和业务, - 掘金

zabbix分布式监控平台zabbix监控系统概述 zabbix 是什么? zabbix 是一个基于 Web 界面的提供 - 掘金

zabbix介绍及部署(超详细讲解) - 靖安yj - 博客园

工作流程:

Zabbix 在进行监控时,zabbix 客户端要安装在被监控设备上,负责定期收集数据,并将其发送给 zabbix 服务端;

zabbix 服务端要安装在监控设备上,其将 zabbix 客户端发送的数据存储的数据库中,zabbix web 根据数据在前端进行展示和绘图。

工作模式:

主动模式:zabbix 客户端主动向 zabbix server请求监控项列表,并主动将监控项内需要的数据提交给 zabbix server。

被动模式:zabbix server向 agent 请求获取监控项的数据,zabbix agent 返回数据。

1
zabbix 的主动和被动模式是以 zabbxi 客户端为基准的。

五个模块。

  1. Zabbix_Server:zabbix_server作为核心组件,用来获取agent存活情况和监控数据。所有的配置、统计、操作数据均通过server进行存取到database;
  2. Zabbix_Database:用户存储所有的zabbix的配置信息、监控数据的数据库;
  3. Zabbix_Web:zabbix的web界面,管理员通过web界面管理zabbix配置以及查看zabbix相关监控信息,通常与zabbix_server运行在同一台主机上,也可以单独部署在独立的服务器上;
  4. Zabbix_Proxy:通常用于分布式监控,代理zabbix_server收集部分被监控的数据并统一发送给server端;(通常大于500台主机需要使用)
  5. Zabbix_Agent:部署在被监控主机上,负责收集被监控主机的数据,并发送给servre端或者proxy端;

Zabbix Server、Proxy、Agent都有自己的配置文件以及log文件,重要的参数需要在这里配置,后面会详细说明。

六个进程。

在默认的情况下,zabbix有6个工作进程;分别是 zabbix_agentd,zabbix_get,zabbix_proxy,zabbix_sender,zabbix_server 和 zabbix_gateway。

其中,zabbix_java_gateway 是可选进程。这6个进程的作用如下:

  1. zabbix-agentd 为 zabbix 客户端守护进程 ,主要负责收集客户端监控项数据。
  2. zabbix_server 为zabbix服务端守护进程,主要负责收集zabbix客户端数据。(端口为10051)
  3. zabbix_proxy是zabbix的代理程序,其功能类似于server,作用上类似于一个中转站,最终会把收集的数据再次提交给zabbix_server。
  4. zabbix_get作为zabbix工具,通常运行在zabbix_server或者zabbix_proxy上,用于远程获取客户端信息,通常用于排错。
  5. zabbix_sender也是zabbix的一个工具,通常运行在zabbix的客户端,用于耗时比较长的检查,其作用是主动发送数据。
  6. zabbix_java_gateway是zabbix2.0以后引入的新功能,可以用于JAVA方面的设备;但是只能主动获取数据,而不能被动获取数据。

三种框架。

在实际的工作环境中,根据网络环境和监控的规模不同,zabbix一共有三种框架,分别是server_client架构、master_node_client架构和server_proxy_client架构。

1、server_client架构

zabbix最简单的架构,监控设备和被监控设备之间直接相连,zabbix_server 和 zabbix_client 之间直接进行数据交互。

2、zabbix_proxy_client架构

proxy是连接 server 和 client 之间的桥梁,其本身不存放数据,只是将zabbix_agent端发来的数据暂存,然后再提交给server。这种架构一般用于跨机房、跨网络的中型网络架构。

在server_proxy_client架构中,server设备的宕机会导致整个系统瘫痪而无法正常工作。

3、master_node_client架构

master_node_client架构是zabbix最复杂的架构。一般用于跨机房、跨网络、监控设备较多的大型网络架构。与server_proxy_client架构相比,master_node_client架构的主要区别在于node与proxy上.

在master_node_client架构中,每个node可以理解为一个小的server端,在自己的配置文件和数据库,node下游可以直接连接client,也可以再次经过proxy代理后连接client。

在master_node_client架构中,master设备宕机不会影响node节点的正常工作。

2025 年 11 月 11 日

zabbix介绍及部署(超详细讲解) - 靖安yj - 博客园

2025 年 11 月 10 日

Zabbix:企业级开源监控解决方案

zabbix分布式监控平台zabbix监控系统概述 zabbix 是什么? zabbix 是一个基于 Web 界面的提供 - 掘金

1
2
3
Zabbix 是一个企业级开源分布式监控解决方案。

Zabbix 是一种监控网络众多参数以及服务器、虚拟机、应用程序、服务、数据库、网站、云等健康状况和完整性的软件。Zabbix 使用灵活的通知机制,允许用户为几乎任何事件配置基于电子邮件的警报,从而快速响应服务器问题。Zabbix 提供基于存储数据的优秀报告和数据可视化功能,使其非常适合容量规划。

Zabbix 安装配置

安装 zabbix-java-gateway。

先上传 zabbix-java-gateway.latest.tar。

docker 加载镜像。

1
docker load <zabbix-java-gateway.latest.tar

docker 启动。

1
docker run --name zabbix-java-gateway -t --restart=unless-stopped -p 10052:10052 -e TZ=Asia/Shanghai -d zabbix/zabbix-java-gateway:latest
1
2
3
4
5
docker run --name zabbix-java-gateway -t \    # 容器命名和伪终端
--restart=unless-stopped \ # 重启策略
-p 10052:10052 \ # 端口映射
-e TZ=Asia/Shanghai \ # 时区设置
-d zabbix/zabbix-java-gateway:latest # 后台运行最新镜像

Zabbix Java Gateway 是一个独立的守护进程,专门用于监控 Java应用程序JMX(Java Management Extensions) 指标。

安装 zabbix-server

上传 zabbix-server-mysql.ol-6.4.13.tar。

docker 加载镜像。

1
docker load <zabbix-server-mysql.ol-6.4.13.tar
1
2
3
4
5
6
7
8
9
10
11
12
[root@localhost admin]# docker load <zabbix-server-mysql.ol-6.4.13.tar
0a628c3f1dfa: Loading layer [==================================================>] 114.3MB/114.3MB
9f123eed27d1: Loading layer [==================================================>] 5.131MB/5.131MB
f1a18cee03fd: Loading layer [==================================================>] 174.6kB/174.6kB
1b8a4a73d477: Loading layer [==================================================>] 328.2kB/328.2kB
4c55089aa5fe: Loading layer [==================================================>] 30.21kB/30.21kB
acb81b6f8559: Loading layer [==================================================>] 4.135MB/4.135MB
4694e9d5278c: Loading layer [==================================================>] 3.072kB/3.072kB
843f0fd7d625: Loading layer [==================================================>] 115.6MB/115.6MB
5f70bf18a086: Loading layer [==================================================>] 1.024kB/1.024kB
6f7ed932e3b9: Loading layer [==================================================>] 24.58kB/24.58kB
Loaded image: zabbix/zabbix-server-mysql:ol-6.4.13

docker启动(注意数据库root密码)。

1
docker run --name zabbix-server-mysql -t -v zabbix_server:/etc/zabbix -e TZ=Asia/Shanghai -e DB_SERVER_HOST="192.168.10.195" -e MYSQL_DATABASE="zabbix" -e MYSQL_USER="zabbix" -e MYSQL_PASSWORD="Zabbix@89s#" -e MYSQL_ROOT_PASSWORD="Mysql@89s#" -e ZBX_JAVAGATEWAY="zabbix-java-gateway" --restart=unless-stopped  -p 10051:10051 --user=root -d zabbix/zabbix-server-mysql:ol-6.4.13
1
2
3
4
5
6
7
8
9
10
11
12
13
docker run --name zabbix-server-mysql -t \        # 容器命名和伪终端
-v zabbix_server:/etc/zabbix \ # 配置卷挂载
-e TZ=Asia/Shanghai \ # 时区设置
-e DB_SERVER_HOST="192.168.10.195" \ # 数据库服务器地址
-e MYSQL_DATABASE="zabbix" \ # 数据库名
-e MYSQL_USER="zabbix" \ # 数据库用户
-e MYSQL_PASSWORD="Zabbix@89s#" \ # 数据库密码
-e MYSQL_ROOT_PASSWORD="Mysql@89s#" \ # root密码(可能不需要)
-e ZBX_JAVAGATEWAY="zabbix-java-gateway" \ # Java Gateway连接
--restart=unless-stopped \ # 重启策略
-p 10051:10051 \ # 服务端口映射
--user=root \ # 运行用户
-d zabbix/zabbix-server-mysql:ol-6.4.13 # 镜像版本

安装 zabbix-web

上传 zabbix-web-nginx-mysql.latest.tar。

docker 加载镜像。

1
docker load < zabbix-web-nginx-mysql.latest.tar
1
2
3
4
5
6
7
[root@localhost admin]# docker load < zabbix-web-nginx-mysql.latest.tar
b5221e4692d2: Loading layer [==================================================>] 98.55MB/98.55MB
72523de8d6bb: Loading layer [==================================================>] 27.14kB/27.14kB
da371a8f2857: Loading layer [==================================================>] 143.6MB/143.6MB
5f70bf18a086: Loading layer [==================================================>] 1.024kB/1.024kB
ce903e83adf6: Loading layer [==================================================>] 11.78kB/11.78kB
Loaded image: zabbix/zabbix-web-nginx-mysql:latest

docker启动(注意数据库 root 密码)。

1
docker run --name zabbix-web-nginx-mysql -t -e PHP_TZ="Asia/Shanghai" -e ZBX_SERVER_HOST="192.168.10.195" -e DB_SERVER_HOST="192.168.10.195" -e MYSQL_DATABASE="zabbix" -e MYSQL_USER="zabbix" -e MYSQL_PASSWORD="Zabbix@89s#" -e MYSQL_ROOT_PASSWORD="Mysql@89s#" -p 8080:8080 --restart unless-stopped --user=root -d zabbix/zabbix-web-nginx-mysql:latest
1
2
3
4
5
6
7
8
9
10
11
12
docker run --name zabbix-web-nginx-mysql -t \    # 容器命名和伪终端
-e PHP_TZ="Asia/Shanghai" \ # PHP时区设置
-e ZBX_SERVER_HOST="192.168.10.195" \ # Zabbix服务器地址
-e DB_SERVER_HOST="192.168.10.195" \ # 数据库服务器地址
-e MYSQL_DATABASE="zabbix" \ # 数据库名
-e MYSQL_USER="zabbix" \ # 数据库用户
-e MYSQL_PASSWORD="Zabbix@89s#" \ # 数据库密码
-e MYSQL_ROOT_PASSWORD="Mysql@89s#" \ # root密码(可能不需要)
-p 8080:8080 \ # Web端口映射
--restart unless-stopped \ # 重启策略
--user=root \ # 运行用户
-d zabbix/zabbix-web-nginx-mysql:latest # 最新镜像

默认账号和密码:

1
Admin zabbix

更新容器中 /usr/share/zabbix/include/defines.inc.php,解决名称无法中文问题。

覆盖容器内文件:

1
docker cp defines.inc.php zabbix-web-nginx-mysql:/usr/share/zabbix/include/defines.inc.php

进入容器:

1
docker exec -it zabbix-web-nginx-mysql bash

授权文件:

1
chmod +r /usr/share/zabbix/include/defines.inc.php
1
exit

重启容器:

1
docker restart zabbix-web-nginx-mysql

上传模板

  • 删除 模板中的system.sw.os键值(模板监控项)
  • 先导入zbx_export_templates (34).yaml再导入zbx_export_templates (33).yaml

添加主机群组公司名、公司名/收费站服务器、公司名/车道工控机、公司名/门架工控机/、公司名/门架服务器。

安装 zabbix-sender

上传 zabbix-sender-6.4.7-release1.el8.x86_64.rpm。

安装。

1
rpm -ivh zabbix-sender-6.4.7-release1.el8.x86_64.rpm
1
2
3
4
5
6
7
8
[root@localhost admin]# rpm -ivh zabbix-sender-6.4.7-release1.el8.x86_64.rpm
warning: zabbix-sender-6.4.7-release1.el8.x86_64.rpm: Header V4 RSA/SHA512 Signature, key ID a14fe591: NOKEY
error: Failed dependencies:
libcrypto.so.1.1()(64bit) is needed by zabbix-sender-6.4.7-release1.el8.x86_64
libcrypto.so.1.1(OPENSSL_1_1_0)(64bit) is needed by zabbix-sender-6.4.7-release1.el8.x86_64
libssl.so.1.1()(64bit) is needed by zabbix-sender-6.4.7-release1.el8.x86_64
libssl.so.1.1(OPENSSL_1_1_0)(64bit) is needed by zabbix-sender-6.4.7-release1.el8.x86_64
libssl.so.1.1(OPENSSL_1_1_1)(64bit) is needed by zabbix-sender-6.4.7-release1.el8.x86_64

这个错误表明 Zabbix Sender 需要 OpenSSL 1.1 版本的库,但该系统可能缺少或版本不匹配。

检查当前 OpenSSL 版本。

1
openssl version
1
2
[root@localhost admin]# openssl version
OpenSSL 1.0.2k-fips 26 Jan 2017
1
rpm -qa | grep openssl
1
2
3
4
[root@localhost admin]# rpm -qa | grep openssl
xmlsec1-openssl-1.2.20-5.el7.x86_64
openssl-libs-1.0.2k-8.el7.x86_64
openssl-1.0.2k-8.el7.x86_64

解决办法,不奏效。

1
sudo rpm -ivh compat-openssl10-1.0.2o-4.el8_6.x86_64.rpm
1
2
3
4
5
6
[root@localhost admin]# sudo rpm -ivh compat-openssl10-1.0.2o-4.el8_6.x86_64.rpm
warning: compat-openssl10-1.0.2o-4.el8_6.x86_64.rpm: Header V4 RSA/SHA256 Signature, key ID c21ad6ea: NOKEY
error: Failed dependencies:
crypto-policies is needed by compat-openssl10-1:1.0.2o-4.el8_6.x86_64
openssl < 1:1.1.0 conflicts with compat-openssl10-1:1.0.2o-4.el8_6.x86_64
openssl-libs < 1:1.1.0 conflicts with compat-openssl10-1:1.0.2o-4.el8_6.x86_64

先强制安装 zabbix-sender。

1
sudo rpm -ivh --nodeps zabbix-sender-6.4.7-release1.el8.x86_64.rpm
1
2
3
4
5
[root@localhost admin]# sudo rpm -ivh --nodeps zabbix-sender-6.4.7-release1.el8.x86_64.rpm
warning: zabbix-sender-6.4.7-release1.el8.x86_64.rpm: Header V4 RSA/SHA512 Signature, key ID a14fe591: NOKEY
Preparing... ################################# [100%]
Updating / installing...
1:zabbix-sender-6.4.7-release1.el8 ################################# [100%]

再手动补全缺失的 so 文件。

1
2
3
4
5
6
7
8
# 查找系统现有的openssl库
find /usr/lib64 -name "libssl.so*"
find /usr/lib64 -name "libcrypto.so*"

# 创建软链接(假设您有libssl.so.1.1.1和libcrypto.so.1.1.1)
sudo ln -s /usr/lib64/libssl.so.1.1.1 /usr/lib64/libssl.so.1.1
sudo ln -s /usr/lib64/libcrypto.so.1.1.1 /usr/lib64/libcrypto.so.1.1
sudo ldconfig

最后验证是否修复。

1
ldd $(which zabbix_sender)
1
2
3
4
5
6
7
8
9
10
11
12
[root@localhost admin]# ldd $(which zabbix_sender)
linux-vdso.so.1 => (0x00007ffd227a0000)
libz.so.1 => /lib64/libz.so.1 (0x00007fe792969000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fe79274d000)
libssl.so.1.1 => not found
libcrypto.so.1.1 => not found
libm.so.6 => /lib64/libm.so.6 (0x00007fe79244a000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007fe792245000)
libresolv.so.2 => /lib64/libresolv.so.2 (0x00007fe79202b000)
libpcre2-8.so.0 => /lib64/libpcre2-8.so.0 (0x00007fe791db4000)
libc.so.6 => /lib64/libc.so.6 (0x00007fe7919f0000)
/lib64/ld-linux-x86-64.so.2 (0x000055f119672000)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[root@localhost admin]# find /usr/lib64 -name "libssl.so*" -o -name "libcrypto.so*"
/usr/lib64/libcrypto.so.1.0.2k
/usr/lib64/libcrypto.so.10
/usr/lib64/libssl.so.1.0.2k
/usr/lib64/libssl.so.10

[root@localhost admin]# ls -la /usr/lib64/libssl.so*
lrwxrwxrwx. 1 root root 16 Nov 5 01:10 /usr/lib64/libssl.so.10 -> libssl.so.1.0.2k
-rwxr-xr-x. 1 root root 470336 Aug 3 2017 /usr/lib64/libssl.so.1.0.2k

[root@localhost admin]# ls -la /usr/lib64/libcrypto.so*
lrwxrwxrwx. 1 root root 19 Nov 5 01:10 /usr/lib64/libcrypto.so.10 -> libcrypto.so.1.0.2k
-rwxr-xr-x. 1 root root 2512448 Aug 3 2017 /usr/lib64/libcrypto.so.1.0.2k

[root@localhost admin]# rpm -qa | grep openssl
xmlsec1-openssl-1.2.20-5.el7.x86_64
openssl-libs-1.0.2k-8.el7.x86_64
openssl-1.0.2k-8.el7.x86_64

该系统是 CentOS 7,安装的是 OpenSSL 1.0.2k,但 Zabbix Sender 需要的是 OpenSSL 1.1.x。这就是版本不匹配的根本原因。

接下来整体的解决思路是这样的:

方案一:安装 OpenSSL 1.1(推荐)

由于有内网 yum 源,直接安装 OpenSSL 1.1:

1
2
3
4
5
6
7
# 安装 OpenSSL 1.1
sudo yum install openssl11 openssl11-libs

# 创建符号链接
sudo ln -sf /usr/lib64/openssl11/libssl.so.1.1 /usr/lib64/libssl.so.1.1
sudo ln -sf /usr/lib64/openssl11/libcrypto.so.1.1 /usr/lib64/libcrypto.so.1.1
sudo ldconfig

方案二:如果方案一失败,使用兼容方案

1
2
3
4
# 创建兼容性符号链接(将1.0链接为1.1)
sudo ln -sf /usr/lib64/libssl.so.1.0.2k /usr/lib64/libssl.so.1.1
sudo ln -sf /usr/lib64/libcrypto.so.1.0.2k /usr/lib64/libcrypto.so.1.1
sudo ldconfig

方案三:下载 CentOS 7 兼容的 Zabbix Sender

1
2
3
4
5
# 卸载当前的错误版本
sudo rpm -e zabbix-sender

# 下载 CentOS 7 兼容的版本(如果有)
# 或者从 Zabbix 官网下载对应 CentOS 7 的 RPM 包

立即验证,验证是否修复。

1
ldd $(which zabbix_sender)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[root@localhost admin]# ldd $(which zabbix_sender)
/bin/zabbix_sender: /lib64/libcrypto.so.1.1: version `OPENSSL_1_1_0' not found (required by /bin/zabbix_sender)
/bin/zabbix_sender: /lib64/libssl.so.1.1: version `OPENSSL_1_1_1' not found (required by /bin/zabbix_sender)
/bin/zabbix_sender: /lib64/libssl.so.1.1: version `OPENSSL_1_1_0' not found (required by /bin/zabbix_sender)
linux-vdso.so.1 => (0x00007fff7bff9000)
libz.so.1 => /lib64/libz.so.1 (0x00007fa037cc2000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fa037aa6000)
libssl.so.1.1 => /lib64/libssl.so.1.1 (0x00007fa037833000)
libcrypto.so.1.1 => /lib64/libcrypto.so.1.1 (0x00007fa0373d2000)
libm.so.6 => /lib64/libm.so.6 (0x00007fa0370d0000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007fa036ecb000)
libresolv.so.2 => /lib64/libresolv.so.2 (0x00007fa036cb1000)
libpcre2-8.so.0 => /lib64/libpcre2-8.so.0 (0x00007fa036a3a000)
libc.so.6 => /lib64/libc.so.6 (0x00007fa036676000)
/lib64/ld-linux-x86-64.so.2 (0x0000564d9803d000)
libgssapi_krb5.so.2 => /lib64/libgssapi_krb5.so.2 (0x00007fa036429000)
libkrb5.so.3 => /lib64/libkrb5.so.3 (0x00007fa036141000)
libcom_err.so.2 => /lib64/libcom_err.so.2 (0x00007fa035f3c000)
libk5crypto.so.3 => /lib64/libk5crypto.so.3 (0x00007fa035d09000)
libkrb5support.so.0 => /lib64/libkrb5support.so.0 (0x00007fa035afa000)
libkeyutils.so.1 => /lib64/libkeyutils.so.1 (0x00007fa0358f6000)
libselinux.so.1 => /lib64/libselinux.so.1 (0x00007fa0356ce000)
libpcre.so.1 => /lib64/libpcre.so.1 (0x00007fa03546c000)

查看。

1
zabbix_sender --version
1
2
3
4
[root@localhost admin]# zabbix_sender --version
zabbix_sender: /lib64/libcrypto.so.1.1: version `OPENSSL_1_1_0' not found (required by zabbix_sender)
zabbix_sender: /lib64/libssl.so.1.1: version `OPENSSL_1_1_1' not found (required by zabbix_sender)
zabbix_sender: /lib64/libssl.so.1.1: version `OPENSSL_1_1_0' not found (required by zabbix_sender)

目前还是这样的问题。

考虑到生产环境下在内网中操作,最可靠的解决方案还是:重新安装兼容 CentOS 7 的 Zabbix Sender。

1
2
3
4
5
# 卸载当前版本
sudo rpm -e zabbix-sender

# 安装 CentOS 7 兼容的 Zabbix 版本
# 从 Zabbix 官网下载对应版本:https://www.zabbix.com/download

Download and install Zabbix

搞半天安装的 zabbix-sender-6.4.7-release1.el8.x86_64.rpm是问题的根源,这是为 CentOS 8 构建的包,但我的系统是 CentOS 7

1
2
3
4
5
被监控主机 ──────────────────> Zabbix Server ─────────────────> 用户
│ │ │
├─ zabbix-agent2 (被动采集) │ │
├─ zabbix-sender (主动上报) ├─ 数据处理/存储 ├─ zabbix-web (界面)
└─ Java应用 ─> Java Gateway └─ 告警判断 └─ 图表展示

各组件端口说明。

组件 默认端口 方向 用途
zabbix-server 10051 入站 接收 agent/sender 数据
zabbix-agent2 10050 出站 向 server 发送数据
zabbix-java-gateway 10052 入站 接收 JMX 监控数据
zabbix-web 80/443 入站 Web 管理界面
1
netstat -natp | grep zabbix

先跳过。

image-20251111115156300

安装 zabbix-agent2

Centos

上传zabbix-agent2-6.4.7-releasel.e18.x86_64.rpm

安装rpm -ivh zabbix-agent2-6.4.7-release1.el8.x86_64.rpm

vi /etc/zabbix/zabbix_agent2.conf

主要修改以下配置:

Server=::/0 #允许所有地址访问

ServerActive=10.14.210.139 #Zabbix Proxy地址

Hostname=Zabbix Server 1 #需要与Zabbix Server的配置一致

默认监听端口是10050,不用修改。

设置开机启动systemctl enable zabbix-agent2

启动systemctl start zabbix-agent2

查看日志,看看是否运行正常

tail -f /var/log/zabbix/zabbix_agent2.log

或者看看端口是否启动

netstat -tanlp |grep 10050

Window Server

浏览器 输入127.0.0.1:9009

打开服务services.msc

ServerActive 改为 10.14.128.76:12099

Server 改为 ::/0

容器

上传zabbix-agent2-alpine-6.4-latest.tar

加载镜像docker load <zabbix-agent2-alpine-6.4-latest.tar

启动容器(替换ip这里需要两个ip,后边的ip需要启动后查看报错ip并替换重启)

docker run –name zabbix-agent2 -t -e TZ=Asia/Shanghai -e ZBX_SERVER_HOST=”10.14.139.226” -e ZBX_HOSTNAME=”Zabbix server” -v /var/run/docker.sock:/var/run/docker.sock:ro -p 10050:10050 –restart=unless-stopped -d zabbix/zabbix-agent2:alpine-6.4-latest

收费系统

2026 年 1 月 13 日

1
2
3
C:\WINDOWS\system32>ssh -T git@101.227.48.102
git@101.227.48.102's password:
Connection closed by 101.227.48.102 port 22
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
C:\WINDOWS\system32>ssh -T -v git@101.227.48.102
OpenSSH_for_Windows_9.5p1, LibreSSL 3.8.2
debug1: Reading configuration data C:\\Users\\Lenovo/.ssh/config
debug1: Connecting to 101.227.48.102 [101.227.48.102] port 22.
debug1: Connection established.
debug1: identity file C:\\Users\\Lenovo/.ssh/id_rsa type 0
debug1: identity file C:\\Users\\Lenovo/.ssh/id_rsa-cert type -1
debug1: identity file C:\\Users\\Lenovo/.ssh/id_ecdsa type -1
debug1: identity file C:\\Users\\Lenovo/.ssh/id_ecdsa-cert type -1
debug1: identity file C:\\Users\\Lenovo/.ssh/id_ecdsa_sk type -1
debug1: identity file C:\\Users\\Lenovo/.ssh/id_ecdsa_sk-cert type -1
debug1: identity file C:\\Users\\Lenovo/.ssh/id_ed25519 type 3
debug1: identity file C:\\Users\\Lenovo/.ssh/id_ed25519-cert type -1
debug1: identity file C:\\Users\\Lenovo/.ssh/id_ed25519_sk type -1
debug1: identity file C:\\Users\\Lenovo/.ssh/id_ed25519_sk-cert type -1
debug1: identity file C:\\Users\\Lenovo/.ssh/id_xmss type -1
debug1: identity file C:\\Users\\Lenovo/.ssh/id_xmss-cert type -1
debug1: identity file C:\\Users\\Lenovo/.ssh/id_dsa type -1
debug1: identity file C:\\Users\\Lenovo/.ssh/id_dsa-cert type -1
debug1: Local version string SSH-2.0-OpenSSH_for_Windows_9.5
debug1: Remote protocol version 2.0, remote software version OpenSSH_8.0
debug1: compat_banner: match: OpenSSH_8.0 pat OpenSSH* compat 0x04000000
debug1: Authenticating to 101.227.48.102:22 as 'git'
debug1: load_hostkeys: fopen C:\\Users\\Lenovo/.ssh/known_hosts2: No such file or directory
debug1: load_hostkeys: fopen __PROGRAMDATA__\\ssh/ssh_known_hosts: No such file or directory
debug1: load_hostkeys: fopen __PROGRAMDATA__\\ssh/ssh_known_hosts2: No such file or directory
debug1: SSH2_MSG_KEXINIT sent
debug1: SSH2_MSG_KEXINIT received
debug1: kex: algorithm: curve25519-sha256
debug1: kex: host key algorithm: ssh-ed25519
debug1: kex: server->client cipher: chacha20-poly1305@openssh.com MAC: <implicit> compression: none
debug1: kex: client->server cipher: chacha20-poly1305@openssh.com MAC: <implicit> compression: none
debug1: expecting SSH2_MSG_KEX_ECDH_REPLY
debug1: SSH2_MSG_KEX_ECDH_REPLY received
debug1: Server host key: ssh-ed25519 SHA256:CPXsWA3qquwUoOVeMDDaHHbgsc1zmJWrM01Yoy6hMmI
debug1: load_hostkeys: fopen C:\\Users\\Lenovo/.ssh/known_hosts2: No such file or directory
debug1: load_hostkeys: fopen __PROGRAMDATA__\\ssh/ssh_known_hosts: No such file or directory
debug1: load_hostkeys: fopen __PROGRAMDATA__\\ssh/ssh_known_hosts2: No such file or directory
debug1: Host '101.227.48.102' is known and matches the ED25519 host key.
debug1: Found key in C:\\Users\\Lenovo/.ssh/known_hosts:10
debug1: rekey out after 134217728 blocks
debug1: SSH2_MSG_NEWKEYS sent
debug1: expecting SSH2_MSG_NEWKEYS
debug1: SSH2_MSG_NEWKEYS received
debug1: rekey in after 134217728 blocks
debug1: get_agent_identities: ssh_get_authentication_socket: No such file or directory
debug1: Will attempt key: C:\\Users\\Lenovo/.ssh/id_rsa RSA SHA256:18HfOCQvT8zCV5AVrzxYRzhwO4W0HB0pOhzCk8klaLw
debug1: Will attempt key: C:\\Users\\Lenovo/.ssh/id_ecdsa
debug1: Will attempt key: C:\\Users\\Lenovo/.ssh/id_ecdsa_sk
debug1: Will attempt key: C:\\Users\\Lenovo/.ssh/id_ed25519 ED25519 SHA256:314LS48LcdNWWV6CRzNlB0/oeBGMioEXuiYGuMtMAAM
debug1: Will attempt key: C:\\Users\\Lenovo/.ssh/id_ed25519_sk
debug1: Will attempt key: C:\\Users\\Lenovo/.ssh/id_xmss
debug1: Will attempt key: C:\\Users\\Lenovo/.ssh/id_dsa
debug1: SSH2_MSG_EXT_INFO received
debug1: kex_input_ext_info: server-sig-algs=<ssh-ed25519,ssh-rsa,rsa-sha2-256,rsa-sha2-512,ssh-dss,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521>
debug1: SSH2_MSG_SERVICE_ACCEPT received
debug1: Authentications that can continue: publickey,gssapi-keyex,gssapi-with-mic,password
debug1: Next authentication method: publickey
debug1: Offering public key: C:\\Users\\Lenovo/.ssh/id_rsa RSA SHA256:18HfOCQvT8zCV5AVrzxYRzhwO4W0HB0pOhzCk8klaLw
debug1: Authentications that can continue: publickey,gssapi-keyex,gssapi-with-mic,password
debug1: Trying private key: C:\\Users\\Lenovo/.ssh/id_ecdsa
debug1: Trying private key: C:\\Users\\Lenovo/.ssh/id_ecdsa_sk
debug1: Offering public key: C:\\Users\\Lenovo/.ssh/id_ed25519 ED25519 SHA256:314LS48LcdNWWV6CRzNlB0/oeBGMioEXuiYGuMtMAAM
debug1: Authentications that can continue: publickey,gssapi-keyex,gssapi-with-mic,password
debug1: Trying private key: C:\\Users\\Lenovo/.ssh/id_ed25519_sk
debug1: Trying private key: C:\\Users\\Lenovo/.ssh/id_xmss
debug1: Trying private key: C:\\Users\\Lenovo/.ssh/id_dsa
debug1: Next authentication method: password
git@101.227.48.102's password:
1
2
3
4
5
6
7
8
9
10
11
你好,我需要通过 SSH 密钥访问 GitLab 的 Git 仓库。

我的公钥是:
ssh-rsa AAAAB3NzaC1yc2BAAA...(你的公钥内容)

公钥指纹是:SHA256:18Hf0CQvT8zCV5AVrzXYRzhw04W0HB8p0hzCk8kLaLw

请帮我将以上公钥添加到服务器的 git 用户的 ~/.ssh/authorized_keys 文件中,
并确认 SSH 服务的 PubkeyAuthentication 设置为 yes

这样我就不需要每次都输入密码了。

2025 年 11 月 17 日

1
glpat-vtQtz1YtzToB6L7W5Cgn

添加了个访问令牌,拉取代码还是报错,算了解决下SSH密钥配置问题吧。

本机重新生成SSH密钥。

1
ssh-keygen -t rsa -b 4096 -C "your_email@example.com

Gitlab平台配置该SSH密钥。

2025 年 11 月 13 日

有哪些常见的Docker容器操作命令?掌握这些Docker命令,让容器管理事半功倍-腾讯云开发者社区-腾讯云

【腾讯文档】加班统计表 https://docs.qq.com/sheet/DTEFLY3p2R0tQV0pU?tab=000001

history 模块是一个专注于时序数据管理的模块,它通过合理的数据分类存储策略,为系统提供了高效的历史监控数据管理能力。该模块的设计充分考虑了大数据量、高并发场景下的性能需求,通过批量操作、增量同步和定期清理等机制,保证了系统的稳定性和响应速度。

从业务角度看,该模块为整个监控系统提供了数据持久化和历史查询的基础设施,支持上层应用进行趋势分析、报表生成等高级功能。

knowledge 模块是一个典型的知识库管理系统实现,采用了标准的三层架构和清晰的对象职责划分。该模块提供了知识库内容的完整CRUD操作和基于树形结构的分类菜单管理功能。

代码整体设计规范,使用了MyBatis-Plus等主流框架提高开发效率,同时预留了数据校验等扩展点。虽然存在一些小的代码问题,但整体实现了完整的业务功能,能够满足知识库管理的基本需求。

Section 模块主要提供高速公路交通监控系统的数据统计、分析和展示功能,支持:

  • 实时监控设备状态和性能指标
  • 告警分析和趋势预测
  • 数据传输质量监控
  • 大屏可视化展示
  • 支持决策分析和问题定位
1
生产环境下常用的 Docker 命令

sync 模块是TOMM管理系统中的数据同步核心模块,主要负责接收和处理来自路段侧的数据同步请求,实现省中心与路段侧的数据一致性和实时性。该模块支持多种数据类型的同步,包括部门、资产、告警、监控项等18种不同类型的数据。

system 模块是整个系统的核心模块,主要负责以下关键业务功能:

  1. 资产管理 :提供资产信息的CRUD操作,支持资产分类管理、关联关系维护、资产详情记录等功能,区分省中心和路段两级数据管理模式。
  2. 告警管理 :实现告警数据的查询、统计、趋势分析、历史记录管理等功能,支持告警级别分类、告警策略匹配、告警关闭和恢复处理。
  3. 系统配置 :提供参数配置的CRUD操作、缓存管理、键名唯一性校验等功能,支持系统参数的动态配置和管理。
  4. 路段管理 :实现路段信息的基础CRUD操作,支持路段编号、名称、授权码、服务器IP等信息管理。
  5. 用户管理 :提供用户分页查询、列表查询和条件过滤等功能,支持多表关联查询。

2025 年 11 月 12 日

已使用的微服务相关组件:

  1. Spring Cloud OpenFeign
    • 项目中通过 @EnableFeignClients 启用了Feign客户端
    • 实现了 SectionApiClient 接口用于服务间调用
    • 配置了动态Feign客户端( DynamicFeignClientConfiguration )
    • 值得注意的是,项目禁用了负载均衡功能( FeignNoLoadBalanceConfig )
  2. 分布式消息队列 - Kafka
    • 在多个配置文件中都配置了Kafka相关参数
    • 用于系统间的数据传输和事件驱动
  3. 分布式任务调度 - XXL-Job
    • 项目包含了 tomm-xxl-job-admin 模块
    • 用于管理和调度分布式任务
  4. 分布式缓存和锁 - Redis/Redisson
    • 使用Redisson客户端操作Redis
    • 实现分布式锁、分布式限流等功能
  5. 分布式日志追踪 - TLog
    • 支持分布式链路日志记录和追踪
  6. 服务监控 - Spring Boot Admin
    • 用于监控各个服务的运行状态

缺少的典型微服务组件。

项目中没有发现以下典型的微服务核心组件:

  1. 服务注册与发现 :没有使用Eureka、Nacos、Consul或Zookeeper等
  2. 配置中心 :没有使用Spring Cloud Config或Nacos Config等
  3. API网关 :没有使用Spring Cloud Gateway或Zuul等
  4. 服务熔断和限流 :没有使用Sentinel、Hystrix或Resilience4j等

项目架构特点:

  1. 多模块单体架构 :虽然项目分为多个模块(tomm-admin、tomm-system、tomm-common等),但这些更像是功能模块的划分,而非独立部署的微服务
  2. 分布式能力增强 :项目集成了多种分布式系统常用组件,具备一定的分布式处理能力
  3. 动态服务调用 :通过动态Feign客户端,可以灵活调用不同的外部服务
  4. 容器化支持 :提供了Docker和Docker Compose配置,支持容器化部署
  5. 数据库集群支持 :支持多种数据库,具备处理大数据量的能力

    总结

    该项目是一个增强型的单体应用 ,它采用了多模块的结构设计,并集成了多种分布式系统的常用组件,以提高系统的可靠性、可扩展性和维护性。虽然它没有完整的微服务架构,但已经具备了一些微服务的特性和能力。这种架构适合业务相对稳定、团队规模适中的项目,可以在保持开发效率的同时,为未来可能的微服务拆分打下基础。

2025 年 11 月 11 日

http://101.227.48.102:10080/

http://101.227.48.102:10080/tangxinghua/tomm-mgt-server/-/tree/feature-v2

高速公路联网收费运行监测管理平台 - 大屏

高速公路联网收费运行监测管理平台 - 后台管理

指定拉取 feature-v2 分支:

1
2
export GIT_SSL_NO_VERIFY=true
git clone -b feature-v2 http://101.227.48.102:10080/tangxinghua/tomm-mgt-server.git
1
2
dengrui1
Dr@20251107

image-20251111145555202

这四个项目构成了一个从数据采集 -> 智能分析 -> 综合管理 -> 场景应用的完整闭环:

  1. gantry-sync-server数据源头,负责采集门架基础数据。
  2. tomm-aiops-server智慧引擎,对数据进行分析,产生智能洞察。
  3. tomm-mgt-server指挥中心,基于洞察进行全面的运维决策和管理。
  4. tomm-rmgt-server前线哨所,在路段层面提供具体的监控视图和操作界面。

看会儿前端代码。

1
2
3
4
5
6
7
8
9
10
11
# 若依管理系统/开发环境
VUE_APP_BASE_API = '/dev-api'

# 应用访问路径 例如使用前缀 /admin/
VUE_APP_CONTEXT_PATH = '/'

# 监控地址
VUE_APP_MONITRO_ADMIN = 'http://localhost:9090/admin/login'

# xxl-job 控制台地址
VUE_APP_XXL_JOB_ADMIN = 'http://localhost:9100/xxl-job-admin'

把安装部署搞定,尝试把前后端项目部署并成功访问,再研究下核心业务逻辑。

2025 年 11 月 10 日

(99+ 封私信 / 82 条消息) ERP、OA、CRM到底啥区别?90% 的企业选错了,别再踩坑! - 知乎

ERP(企业资源计划)OA(办公自动化)CRM(客户关系管理)

高速公路联网收费运行监测管理平台

大屏调用接口。

1
2
3
4
5
6
7
8
/**
* 大屏显示的接口 Controller
*/
@Tag(name = "大屏显示的接口")
@RestController
@RequestMapping("/big_screen_displa")
@Anonymous
public class BigScreenDisplaController

资产中心。

1
2
3
4
5
6
7
8
9
/**
* 登录验证
*
* @author Lion Li
*/
@Validated
@RequiredArgsConstructor
@RestController
public class SysLoginController
1
2
3
4
5
6
7
/**
* 获取路由信息
*
* @return 路由信息
*/
@GetMapping("getRouters")
public R<List<RouterVo>> getRouters(Long type)
1
2
3
4
5
6
7
8
9
10
/**
* 资产 Controller
*
* @author zhanghuimin
* @date 2025-08-14
*/
@Tag(name = "资产 Controller")
@RestController
@RequestMapping("v2/asset")
public class AssetController extends BaseController
1
2
3
4
5
6
7
8
9
10
/**
* 资产类型 Controller
*
* @author zhanghuimin
* @date 2025-08-14
*/
@Tag(name = "资产类型 Controller")
@RestController
@RequestMapping("v2/asset_type")
public class AssetTypeController extends BaseController
1
2
3
4
5
6
7
8
9
10
/**
* 部门信息
*
* @author Lion Li
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/system/dept")
public class SysDeptController extends BaseController
1
http://101.227.48.102:10003/dev-api/system/dept/v2/list

今天的任务,在本地把项目跑起来,再根据安装部署手册初步尝试在本地虚拟机安装基础环境。

业务逻辑的话,稍后放一放,姜总说业务和技术也不复杂,多的是查询接口。

本地启动 tmmo-admin 模块。

1
import sun.font.FontDesignMetrics;
1
2
D:\Project\luofeng\tomm-mgt-server-feature-v2\tomm-common\src\main\java\com\tomm\common\utils\QRCodeUtil1.java:11:16
java: 程序包sun.font不存在

出现这个错误,把 JDK 换成 jDK8 即可。

1
Error running 'TommApplication' Error running TommApplication. Command line is too long. Shorten the command line via JAR manifest or via a classpath file and rerun

在启动 TommApplication时遇到了 “Command line is too long” 的错误。这是一个在Windows系统上使用IntelliJ IDEA开发大型Java项目时常见的问题,主要是因为项目的类路径(classpath)过长,超过了操作系统允许的命令行长度限制。

image-20251110165217191

打开运行/调试配置在IDEA顶部工具栏,找到并点击当前显示为 TommApplication的运行/调试配置下拉菜单,然后选择 Edit Configurations...。在配置窗口中,找到并点击 Modify options 按钮,在展开的菜单列表中,勾选 Shorten command line 选项启用缩短命令行功能

完成上一步后,配置界面下方会出现一个名为 Shorten command line 的新下拉菜单。点击它,您会看到两个选择:

  • **JAR manifest**:IDEA会创建一个临时的JAR文件,并将冗长的类路径信息写入其清单文件(MANIFEST.MF)中。这是常用的方法。
  • **classpath file**:IDEA会创建一个文本文件来存储类路径,然后在启动命令中引用该文件。

保存配置,重启应用。

1
2
3
2025-11-10 16:55:20 [main] ERROR o.s.boot.SpringApplication
- Application run failed
org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [com.tomm.TommApplication]; nested exception is org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name 'TMessageTemplateController' for bean class [com.tomm.web.controller.BaseData.TMessageTemplateController] conflicts with existing, non-compatible bean definition of same name and class [com.tomm.system.controller.TMessageTemplateController]
1
Caused by: java.lang.IllegalStateException: either 'jasypt.encryptor.password' or one of ['jasypt.encryptor.private-key-string', 'jasypt.encryptor.private-key-location'] must be provided for Password-based or Asymmetric encryption

这表明应用程序使用了 Jasypt 进行配置加密,但在启动时缺少必要的加密密钥配置。

1
2
3
4
5
6
7
8
9
10
11
12
spring:
redis:
# 地址
host: 127.0.0.1
#host: 124.71.226.0
# 端口,默认为6379
port: 6379
#port: 9088
# 数据库索引
database: 0
# 密码(如没有密码请注释掉)
# password: ENC(BYh3oiwHYjDezan9szQTDu16MB0LcHGM)
1
2
3
4
5
jasypt:
encryptor:
password: lybgeek
algorithm: PBEWithMD5AndDES
iv-generator-classname: org.jasypt.iv.NoIvGenerator

直接改这个配置:

1
2
profiles:
active: dev

TommApplication 应用成功启动。

启动 DataCollectionApplication,出现:

1
2
RedisConnectionException: Unable to connect to Redis server: 101.227.50.186/101.227.50.186:6379
ConnectTimeoutException: connection timed out: 101.227.50.186/101.227.50.186:6379

错误原因是应用程序无法连接到指定的Redis服务器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
--- # redis 单机配置(单机与集群只能开启一个另一个需要注释掉)
spring:
redis:
# 地址
host: 101.227.50.186
#host: 124.71.226.0
# 端口,默认为6379
port: 6379
#port: 9088
# 数据库索引
database: 0
# 密码(如没有密码请注释掉)
#password: 123456
#password: ssljtzzh
password: ENC(G2lSweX0Vk/aZP42SIaKqYPtv4jYjcWa)
# 连接超时时间
timeout: 10s
# 是否开启ssl
ssl: false

直接连接本地 Redis,也不行,这应该是远程 Redis 服务器出现问题了,先不用管。

顺利启动 MonitorAdminApplication 和 XxlJobAdminApplication 应用。

大屏

2025 年 11 月 5 日

高速公路联网收费运行监测管理平台

image-20251105092910030

高速公路联网收费运行监测管理平台

image-20251105093003797

让高速更“高速”!四川成雅高速试点自由流ETC门架

1
收费站,门架

高速公路龙门架收费详解与未来展望

1
“龙门架分段式收费”是一种高速公路收费模式。龙门架,作为布设在两个相邻收费站之间的设备,主要负责计算收费金额和扣费。这种分段收费的方式,是将高速公路依据相邻的互通立交、入/出口收费站划分为若干个独立的收费区间,每个区间都会独立计算费额。而ETC门架的名称,通常以收费区间两端连接点的名称来命名,例如“花城大道-黄埔大道”。
1
图中所示即为龙门架,它会在车辆经过时自动记录行车路径与时间,进而生成详细的通行流水。在收费站出口处,通常只会显示最后一个匝道的费用情况。若该匝道不收费,则显示为0元;若应收费,则显示相应的匝道费额,客户所见的收费记录即为该费用。

ETC车辆通过龙门架时,其收费金额是根据相邻两个收费站的里程、车型对应的收费标准以及折扣优惠来计算的,最终按照“四舍五入”的规则精确到“分”。

对于人工收费车辆,则使用CPC卡来记录行车路径,采用分段计费的方式,在出口合并收费,并同样遵循“四舍五入”的规则取整至“元”。

高速收费模式。

首座ETC门架亮相!淮南人路过这里会发现!

高速龙门架激增,你了解ETC门架的双重功能吗

鼓掌!我省正在运营高速首个ETC门架安装完成!(内附视频)

刚连上数字公司 WIFI 的时候进不了这个系统,现在把手机热点关了连数字公司也行了,省点自己的流量。

(99+ 封私信 / 82 条消息) 高速记费门架上,悬挂“不抓拍违章”醒目标牌,这么做的目的是什么? - 知乎

门架的服务器,工控机还有机柜是什么东西?

三者的关系与协同工作流程

我们可以通过一个简单的流程来理解它们如何协同工作:

  1. 车辆通行:一辆装有ETC设备的汽车通过收费门架。
  2. 数据采集:门架上的RSU天线与车辆OBU进行通信,完成交易;摄像机抓拍车辆图像和车牌信息。
  3. 边缘处理(工控机):每个车道的工控机实时处理本车道设备采集的数据,并生成一条原始的通行记录。
  4. 数据汇聚(门架服务器):所有车道的工控机将通行记录上传至安装在机柜内的门架服务器
  5. 存储与上传:门架服务器将所有数据整合、存储,然后通过通信网络实时上传至省级联网中心平台,用于后台计费、清分结算和运行监测。
  6. 物理保障(机柜):在整个过程中,机柜为内部的工控机门架服务器提供了一个安全、稳定、适宜的运行环境。

省中心,路段公司还有收费站三者之间有什么关系?

所以,它们三者的关系是:

  • 收费站路段公司负责,是收入的直接“采集器”。
  • 路段公司省中心负责,是具体业务的“经营者”和“执行者”。
  • 省中心对所有 路段公司收费站进行管理和协调,是规则的“制定者”和资金的“清算者”。

保存下大屏页面截图。

1
同事让我研究下高速公路联网收费运行监测管理平台,以及对应的大屏。你就是最红我有些东西确实不太明确,不过你从这个系统中可以告我些什么信息呢。

这个项目一期二期在指什么?

根据您之前提供的平台信息,同事口中的“一期、二期”很可能指:

  • 一期:指当前已经上线的这个高速公路联网收费运行监测管理平台本身,它实现了对全省路网、设备、收费业务的初步集中监测和告警。
  • 二期:可能是在讨论下一步的升级计划。例如:“二期我们准备把所有的门架设备监控也接进来。”“这个AI分析功能得等到二期才能开发。”“目前大屏显示的是基础数据,二期的可视化效果会更棒。”

作为后台开发人员,在这份工作中,我可能会负责什么样的任务?

你的工作虽然不在收费一线,但却是整个收费系统的大脑和心脏。你每天打交道的主要是服务器、数据库、数据流水、业务参数。你的核心价值在于保障资金准确、保障系统稳定、保障规则生效、保障数据有用

智慧收费 | 高速公路联网收费运行态势智慧感知系统研究及应用-腾讯云开发者社区-腾讯云

公路收费系统。

1
公路收费系统采用省收费中心、路段分中心、收费站的三级管理体制 [1]。底层车道系统具备车型判别、信息校验、计费核算功能,支持ETC、移动支付等自动缴费方式。收费站系统负责数据查询、票据管理及图像稽查,并向下辖车道终端传输基础参数。省级联网中心通过"芸路通""芸数聚"等平台实现全路网数据汇总与通行费拆分结算。

公路收费系统是基于信息技术实现道路通行费征收管理的专业化软件体系,其建设与推广受国务院深化收费公路制度改革部署推动。系统通过车道终端设备、收费站管理平台及省级联网系统的三级架构,集成ETC电子不停车收费、移动支付、车牌识别等技术手段,有效提升通行效率并降低运营成本。截至2019年底,全国已实现ETC用户突破1.8亿、货车不停车收费等政策目标,人工车道全面支持移动支付方式 [1]。杭州恒生芸擎、北京捷迅贵德等企业开发的收费系统解决方案,采用”云 边 端”架构与智能分析技术,在浙江等省份实现高效处理能力。

中午十二点吃个饱饭,睡了二十分钟到两点十分醒来。

Jetbrains正式宣布免费,有点猛啊!提到 Jetbrains 这家公司,相信搞开发的同学应该都不陌生。 该公司盛产 - 掘金

软考

2026 年 2 月 27 日

信息系统项目管理师综合知识章节题库 - 信管网

信管网,刷题。

2026 年 2 月 25 日

全国计算机技术与软件专业技术资格(水平)考试

2025 年 11 月 7 日

中国计算机技术职业资格网

希赛网_专注在线职业教育24年

明年上半年可以考信息系统项目管理师高项,明年下半年可以考系统架构设计师高项,计划用一年的时间拿下两本软考高级证书。

中级暂不考虑,已经有软件设计师中项了,

积分入户_百度百科

1
积分入户是中国部分城市基于积分指标体系审核外来务工人员落户资格的户籍管理政策。

(99+ 封私信 / 82 条消息) 信息系统项目管理师证书(高项)有什么用? - 知乎

端口超限

2026 年 1 月 12 日

1
2
3
http://101.227.48.102:10080/
dengrui1
Dr@20251107

2025 年 11 月 7 日

http://101.227.48.102:10080/

http://101.227.48.102:10080/tangxinghua/tomm-mgt-server/-/tree/feature-v2

1
2
3
4
5
6
Lenovo@LAPTOP-5U3S75BI MINGW64 /d/桌面/新建文件夹
$ git clone http://101.227.48.102:10080/tangxinghua/tomm-mgt-server/-/tree/feature-v2
Cloning into 'feature-v2'...
fatal: unable to update url base from redirection:
asked for: http://101.227.48.102:10080/tangxinghua/tomm-mgt-server/-/tree/feature-v2/info/refs?service=git-upload-pack
redirect: http://101.227.48.102:10080/users/sign_in
1
2
dengrui1
Dr@20251107

这样直接拉取确实能拉取成功,不过为什么只拉下来个README.md文件呢。。

1
2
export GIT_SSL_NO_VERIFY=true
git clone http://101.227.48.102/zhm/tomm-rmgt-server.git
1
2
export GIT_SSL_NO_VERIFY=true
git clone http://101.227.48.102:10080/tangxinghua/tomm-mgt-server.git
1
2
export GIT_SSL_NO_VERIFY=true
git clone http://101.227.48.102:10080/tangxinghua/tomm-mgt-server.git
1
http://101.227.48.102:10080/tangxinghua/tomm-mgt-server/-/tree/feature-v2.git

解决火狐浏览器的(此网址已被限制,此网址使用了一个通常用于网络浏览以外目的的端口,出于安全原因,Firefox 取消了该请求)问题_火狐此计算机已取消连接-CSDN博客

1
此网址已被限制,此网址使用了一个通常用于网络浏览以外目的的端口,出于安全原因,Firefox 取消了该请求

image-20251107163135662

解决方案:

1、在火狐浏览器中输入 about:config

1
about:config

image-20251107163308546

2、在搜索首选项中输入 network.security.ports.banned.override

1
network.security.ports.banned.override

image-20251107163210956

其中,设置开放端口可以使用,来分隔多个端口;使用-表示一个开放端口范围。

3、测试,访问 http://101.227.48.102:10080/。

image-20251107163534922

image-20251107163553147

其实解决方案 DeepSeek 刚刚已经讲明白了,只不过没图没指导完全没看懂在干什么。

解决方法:修改 Firefox 配置

请严格按照以下步骤操作:

第一步:打开 Firefox 配置编辑器

  1. 在 Firefox 地址栏中,输入 about:config然后按 Enter。
  2. 您会看到一个警告页面,提示“这会失去保修!”或类似内容。点击 “接受风险并继续”

第二步:修改安全配置

  1. 在顶部的搜索栏中,搜索 security.ports
  2. 这会过滤出与端口限制相关的配置项。我们需要修改的是 security.ports.banned.override
  3. 双击 security.ports.banned.override这一项。
  4. 会弹出一个对话框让你输入字符串值。
  5. 在输入框中,填入您需要解除限制的端口号 10080。如果您需要允许多个端口,用英文逗号分隔,例如:10080,8081,9000
  6. 点击 “确定”

第三步:刷新页面或重新访问网址

现在,回到之前被阻止的标签页,刷新页面,或者在新标签页中重新输入网址 http://101.227.48.102:10080。您现在应该可以正常访问该代码仓库了。S

1
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDhWwnOcW2CD7mkj08pnmAxQuPvEoBCIo4lwofyh5Gnf4rrrmL5a1dvilwGxVuSNRCTpdCcwU7gire7ira4kiyu1xrPY21H77jC8hmOkYwJ/xFHs9j3uO2qdv6vk3nzMian8ahiN7EgVDBColQVTPMt0zM33sFaP4X6dvqsMwgDZWbgzbViqo7MDWNvyaCZlb14YSeDQZ4K5oQGyzj5jhLNV/D2PqvSiA4MKiPwMlUMZvuS0NmZWBqoFIbVXeQ21t0ZzIlse+5ogwki0yAJd1mgInAlyEvYYoDNBvNFgjOyHJ9Rc0LbtIQASu90oap9vXdb2AEOZ0cgDaeeISxUrsmc9N/Sc8La49SHBgazO9ClMq84cyBKECzRo6iHh6SjNGsKNdTdiSuzgO7SUrUQ+mGBBSIyKpuGJ3mhlS6ewzRTq3iC1cZoUzl1VD4/Buvxds2BIEVSLzfsqC+/RG8rFc7PFo005eFF9d09INoT8Woype59norGGkWikVodAHYlJUs= Lenovo@LAPTOP-5U3S75BI

image-20251107165634405

1
2
3
4
5
# 生成新的 ED25519 密钥(推荐)
ssh-keygen -t ed25519 -C "your_email@example.com" -f ~/.ssh/id_ed25519_gitlab

# 或者生成 RSA 密钥
ssh-keygen -t rsa -b 4096 -C "your_email@example.com" -f ~/.ssh/id_rsa_gitlab

执行这个,强制指定生成新密钥后的存放路径。

1
ssh-keygen -t ed25519 -C "dengwei@dengwei.com" -f C:\Users\Lenovo\.ssh\id_ed25519_gitlab

生成成功,Gitlab 平台添加 SSH 密钥成功。

1
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFyb7N7m7i2AY301bcIiPO7QkBgDAnR1H/7dVrlQw9K1 dengwei@dengwei.com

image-20251107170410740

image-20251107170441481

测试连接

1
ssh -T git@101.227.48.102
1
为什么这么干了,拉取时还是要求我输入密码啊,

首先验证 SSH 密钥是否正确加载

1
2
3
4
5
# 查看 SSH 代理中是否有您的密钥
ssh-add -l

# 如果没有显示您的密钥,手动添加
ssh-add C:\Users\Lenovo\.ssh\id_ed25519_gitlab # 替换为您的私钥文件路径

出现错误:

1
2
C:\WINDOWS\system32>ssh-add -l
Error connecting to agent: No such file or directory

这个错误表明 SSH 代理没有运行。在 Windows 系统中,SSH 代理的管理方式与 Linux/Mac 不同。

Windows 系统下的 SSH 代理管理,使用 Git Bash:

1
2
3
4
# 在 Git Bash 中执行(不是 CMD)
eval "$(ssh-agent -s)"
ssh-add C:\Users\Lenovo\.ssh\id_ed25519_gitlab
ssh-add -l

依次执行命令。

1
2
3
Lenovo@LAPTOP-5U3S75BI MINGW64 /d/Project/luofeng
$ ssh-add -l
The agent has no identities.
1
2
3
Lenovo@LAPTOP-5U3S75BI MINGW64 /d/Project/luofeng
$ ssh-add C:\Users\Lenovo\.ssh\id_ed25519_gitlab
C:UsersLenovo.sshid_ed25519_gitlab: No such file or directory
1
2
3
Lenovo@LAPTOP-5U3S75BI MINGW64 /d/Project/luofeng
$ ssh-add C:/Users/Lenovo/.ssh/id_ed25519_gitlab
Identity added: C:/Users/Lenovo/.ssh/id_ed25519_gitlab (dengwei@dengwei.com)

还是不行。。

拉取 Gitee 代码就没有什么问题,这个 Gitlab 平台反复配置完毕 SSH 密钥,在拉取仓库代码时还是提示要求输入密码。

直接下载压缩包吧。

代码看到了,但 maven 仓库还没给配。

美元

2025 年 11 月 7 日

人民币购买力竟超美元,生活成本差距惊人,真相让人震惊

美元和人民币之间的实际购买力按盒饭算美元

USB

2025 年 11 月 7 日

[USB Type-C_百度百科](https://baike.baidu.com/item/USB Type-C/16565059)

usb-a和usb-b和usb-c的搜索结果_百度图片搜索

1
DP输出指的是DisplayPort接口的输出功。DisplayPort是由PC及芯片制造商联盟开发,视频电子标准协会标准化的数字式视频接口标准,支持高分辨率、高刷新率和高动态范围技术,如4K、8K等。与HDMI相比,DP具有更高带宽和免费协议等优势。

显示器DP接口和HDMI有什么区别?详细对比一看就懂!

DP 接口和 HDMI 接口。

1
USB 全称为 Universal Serial Bus,翻译过来就是通用串行总线,由英特尔与众多电脑公司提出来用于规范电脑与外部设备的连接与通讯。

(99+ 封私信 / 82 条消息) usb的学习一 - 知乎

USB接口那么多!!你都认识吗??知道他们的区别吗??_mirco b与 usb a_usb接口有几种-CSDN博客

USB Type-A,USB Type–B,USB-Type C 接口。

分清楚各式各样的USB接口!一帖搞定_usb-a接口-CSDN博客

1
USB:Universal Serial Bus,通用串行总线。

USB:Universal Serial Bus,通用串行总线。

HDMI:High Definition Multimedia Interface,高清多媒体接口。

DP:DisplayPort,数字式视频接口标准。DP在这几年也逐渐流行起来了,它是一个由PC及芯片制造商联盟开发,视频电子标准协会(VESA)标准化的数字式视频接口标准,DP 的功能和 HDMI 非常相似,主要用于视频传输。

电脑USB、HDMI、DP各种接口及速度-CSDN博客

(99+ 封私信 / 82 条消息) 一文看懂显示接口:HDMI / DP / VGA / USB-C 有什么区别?怎么选? - 知乎

银行卡

2025 年 11 月 6 日

别再纠结了!社保卡的所属银行是哪个?4种方式都可以查询

银行一类卡和二类卡,你真的了解吗?

中国银行一类卡和二类卡的区别?

有关银行卡的知识,同下。

社保

2025 年 12 月 2 日

太原2025社保计算器_五险一金计算器_微蜂网

1
2
社保11月份没买,公司交的部分,按太原标准发给你,你自己买。
下个月开始,在公司买社保,个人要扣除569.72元,也就是下个月工资是4000=569.72
1
公司买的话,买广州的社保,医保什么的可以全国通用的,你问问慧敏,公司给他买的也是广州社保

按养老保险缴纳8%、医疗保险缴纳2%,工伤保险缴纳0.2%的标准,以自己的全额工资为主,在广州交社保个人缴纳576.54元,公司缴纳部分1408.90元,一共1985.44元。

这个计算结果相当接近了。

如果按太原的标准,那个人缴纳515元,公司缴纳1307.5元,一共1822.5元。

那看来挺划算。

昨天报了中行卡账号,周四晚前就能领到上个月工资了,高兴高兴,一个月辛辛苦苦收入还是不赖的,上个月差旅费还会额外发给我。

1
广州省广州市天河区

2025 年 11 月 6 日

不知道“五险一金”是什么?看这篇就够了

1
“转正后缴纳”:违法!《社会保险法》第五十八条规定,用人单位应当自用工之日起三十日内为其职工向社会保险经办机构申请办理社会保险登记。未办理社会保险登记的,由社会保险经办机构核定其应当缴纳的社会保险费。即使在试用期,用人单位与劳动者协商一致,也不能不缴纳社保费哦!

秒懂“五险一金”原来是这么回事~😲

一口气读懂五险一金

激活社保卡金融功能需本人持有效身份证件和社保卡原件,到卡面所示服务银行的全国任一网点柜台办理,16周岁以下未成年人可由监护人代办。‌ 该流程依据银行监管规定,无法线上办理,且激活时可同步启用社保功能。‌

  • 必备材料‌:本人有效身份证件(如居民身份证)及社保卡原件;16周岁以下未成年人需额外提供监护人身份证件及关系证明(如户口簿、出生医学证明)。‌
  • 办理步骤‌:确认社保卡卡面所示服务银行;本人携带材料至该银行全国任一网点(部分银行支持异地激活);在柜台设置金融账户密码,完成激活。‌
  • 特殊人群‌:未成年人由监护人持双方证件及关系证明代办;行动不便者可联系银行预约上门服务。‌
  • 关键说明‌:必须本人办理(未成年人除外),不可代办或线上操作;激活后金融功能立即生效,用于养老金发放等资金存取;未激活仅社保功能可用;社保功能可通过“电子社保卡”小程序等线上启用,但金融功能需线下办理。‌

1
2
激活后金融功能立即生效,用于养老金发放等资金存取;未激活仅社保功能可用。
这句话什么意思,我入职后正常缴纳五险一金,未激活金融功能的社保卡可以正常使用吗?

答案是:完全可以正常使用。

您入职后,公司为您正常缴纳“五险一金”(其中“五险”就是通过社保卡来关联的),完全不受社保卡金融功能是否激活的影响。

五险一金的缴纳主要通过单位账户完成,与社保卡是否激活无直接关联。即使未激活社保卡,单位仍可正常为员工缴纳社保费用。‌

  • 医保使用:您可以直接用未激活金融功能的社保卡去医院看病、在定点药店买药,医保报销部分会正常结算。
  • 社保缴费记录:您的养老保险、失业保险等缴费记录会正常计入您的个人社保账户,可以通过社保局官网、APP或线下机器查询。

简单来说:金融功能只关系到“钱怎么发到您手上”,而社保功能关系到“您的社保权益和记录”。只要公司为您正常参保,您的社保权益就在持续积累。

虽然不激活金融功能不影响您现阶段使用社保待遇,但还是强烈建议您尽快抽空去银行网点激活它,原因如下:

  1. 未来领取待遇需要:当您未来需要领取养老金、报销生育津贴等现金补贴时,钱会打到这个金融账户。如果账户未激活,钱可能无法正常打入,会影响您领取。
  2. 一卡多用,方便省事:激活后,您就不需要额外提供其他银行卡号给单位或社保机构用于接收各类补贴,一张卡全搞定。
  3. 激活是免费的:携带本人身份证和社保卡,到卡面上所示银行的任意网点(如卡上印着“工商银行”,就去工行网点)即可办理激活手续,设置银行卡密码,整个过程很快。

现在未激活金融功能,完全不影响入职后正常使用社保(看病、买药、积累缴费年限),考虑到未来需要顺利领取各种社保待遇,建议尽早激活金融功能。


1
你说的很清楚明白,我能不能这么理解,不激活完全不影响交社保,激活金融功能后就能将其当作一张储蓄卡来使用了。

社保卡金融功能未激活并不影响交社保用社保,公司为您缴纳社保费用、您用卡看病报销、积累社保权益,这些核心功能完全不受影响。

而金融功能激活后,社保卡就变成了一张标准的银行借记卡,具备所有储蓄卡的功能(存钱、取钱、转账、理财等)。

完全正确。


1
借记卡,储蓄卡,信用卡,一类卡,二类卡……等等这些概念是什么意思啊

银行卡按功能和本质分类,可分为借记卡 vs 信用卡。

储蓄卡、工资卡,这两个名字都是借记卡的俗称。

信用卡,又叫透支卡,银行先给您一个信用额度(比如1万元),您可以在额度内直接花钱,这相当于向银行借了一笔短期贷款。到每个月账单日,您需要把花的钱还给银行,逾期不还会产生高额利息和影响信用。

借记卡/储蓄卡是花自己的钱;信用卡是花银行的钱(要还的)。

银行卡按账户权限分类,又可分为一类卡、二类卡和三类卡

这是中国人民银行(中国的央行)为了管理账户风险而做的规定,主要针对的是借记卡(以及其背后的银行账户)。

您可以把它想象成家里的门:一类卡是大门,二类卡是房门,三类卡是保险柜的小门。

卡类型 形象比喻 功能与限额(年累计限额) 特点与用途
一类卡 家里的“大门” 全能、无限额(理论上受银行单日限额限制,但年累计无上限)。 · 全功能账户。 · 存款、取款、转账、理财、消费,所有功能不受限制。 · 一个人在同一家银行只能开立一个一类户。您的工资卡、主要理财卡通常就是这个。
二类卡 家里的“房门” 有限额: · 存入/转入资金:日限额1万,年累计限额20万。 · 消费/支出:日限额1万,年累计限额20万 · 限制功能账户。 · 适合用于日常小额消费、绑定支付软件(微信/支付宝)、买理财等,资金安全更有保障。 · 一个人在同一家银行可以开多个二类户。
三类卡 家里的“保险柜” 限额很低:账户余额最高不超过2000元,用于小额快捷支付。 · 极度限制账户。 · 主要用于手机扫码支付、闪付等小额消费场景。 · 即使被盗,损失也有限。

重要关系:

  • 您新办的社保卡上的金融账户,默认就是一类卡(只要您在该银行没有其他一类卡)。
  • 如果您在某家银行已经有一张一类卡(比如以前的工资卡),再办新卡(包括社保卡)时,银行可能会询问您是否要将新卡降为二类卡,或者将旧卡降级。

💳社保卡六大实用功能,你知道几个

五险一金到底有啥用?作为子女和打工人,我终于想明白了


接下来的问答涉及到大量的 AI 回复内容,其回复不可谓不精准正确,索性把三次提问得到的结果都贴在下面了,交五险一金到底有什么好处

1
2
都说交社保交五险一金对自己有好处,究竟有什么好处呢,网络上的资源内容来来去去就是那几个字眼:什么养老保险累计交够15年就能在退休后领养老金,医保将来能报销能买药,失业保险交够满一年后可以在被动离职后每月领钱,工伤保险生育保险又能保障自己的安全更人性化……
但是这养老金每月能领多少呢,医保缴够后将来能怎么报销呢,又怎么买药,失业保险又能领多少钱,工伤后怎么申请工伤险呢……等等这些细枝末节的问题我完全不晓得答案。

您这个问题问得非常好,也特别实在!您的感觉完全正确——网络上大部分信息都停留在“有什么”的层面,而您关心的是“怎么用”、“能得多少”这个核心问题。这就像别人只告诉您“汽车能代步”,而您想知道的是“这车怎么开?油耗多少?能跑多快?”。

下面,我将抛开那些笼统的字眼,用最直白的方式为您拆解“五险一金”实实在在的好处和操作细节。


养老保险:退休金到底怎么算?

核心好处:只要累计缴满15年,并达到法定退休年龄,退休后就可以每月领钱,活到老领到老。这个钱还会随着社会平均工资的增长而增加,是退休后最稳定的收入来源。

关键问题:每月能领多少?

计算公式非常复杂,但我们可以用一个“白话版”来理解,它主要取决于三大要素:

  1. “存款”多少(您的缴费基数):您工资越高,缴费基数就越高,个人账户里存的钱就越多。原则是“多缴多得”。
  2. “存款”多久(您的缴费年限):您交的时间越长,账户里的钱也越多。原则是“长缴多得”。交满15年只是门槛,建议一直交到退休
  3. “社会平均工资”(退休时上年度全省市的社会平均工资):您退休时社会的整体工资水平越高,您能领到的基础养老金也越高。

一个极简的估算例子

假设您退休时,当地社会平均工资为8000元/月,您一生的平均缴费工资也是8000元,缴费刚好30年。

  • 基础养老金≈ (8000 + 8000) / 2 × 30 × 1% = 2400元/月
  • 个人账户养老金≈ 您个人账户里全部储存额(您自己缴的部分+利息) ÷ 一个固定的计发月数(如60岁退休是139个月)。假设您个人账户有20万,那么这部分 ≈ 200000 / 139 ≈ 1439元/月

那么您每月总养老金 ≈ 2400 + 1439 = 3839元/月

请注意,这只是一个简化模型,但能帮您理解其构成。实际金额可以通过支付宝/微信中的“社保查询”服务或当地人社APP进行测算。


医疗保险:具体怎么报销和买药?

核心好处:解决“看病贵”的问题。它分为两个账户:

  1. 个人账户(医保卡里的钱)来源:您自己缴纳的医保部分(约2%)会进入这个账户。 怎么用:就像一笔医疗专款,可以用来: 在定点药店刷卡买药(非处方药和部分处方药)。 支付门诊、急诊、住院的起付线以下和个人自付部分的费用
  2. 统筹账户(国家的大池子)来源:公司为您缴纳的大部分医保进入这个公共基金。 怎么用(报销):当您的医疗费用超过一定金额(称为“起付线”或“门槛费”)后,就可以按比例报销。 报销流程:在定点医院看病时,在挂号、缴费时主动出示社保卡(或电子医保凭证),系统会直接进行结算——您只需要支付报销后需要自己掏腰包的那部分,报销部分由医院和医保系统直接结算,您无需垫付再跑报销。 报销比例:通常医院级别越高(如三甲医院),报销比例越低(如70%-85%);社区医院报销比例更高(可能超过90%)。也有年度报销上限(封顶线),一般为几十万元。

失业保险:被动离职后能领多少钱?

核心好处:被公司辞退、合同到期公司不续签等非自愿失业后,提供过渡性的经济支持。

关键问题:能领多少?领多久?

  • 能领多少:各地标准不同,但通常是**当地最低工资的70%-90%**。例如,某市最低工资2320元/月,失业金可能在1600-2100元/月左右。具体金额由当地社保局核定。
  • 领多久:与您缴费时间挂钩。 缴费满1年不足5年:最长领12个月。 缴费满5年不足10年:最长领18个月。 缴费10年以上:最长领24个月。
  • 怎么领: 单位需在解除劳动关系后为您办理“社保停保”,并注明原因是非本人意愿(如辞退)。 您本人在离职后,携带身份证、社保卡等材料到户籍地或参保地的公共就业服务机构办理失业登记。 审核通过后,失业金会按月发放到您的社保卡金融账户或指定银行卡。

工伤保险与生育保险:怎么申请?

(1)工伤保险

  • 场景:在工作时间、工作地点、因工作原因受伤/患病/死亡(包括上下班途中非本人主责的交通事故)。
  • 怎么申请第一时间:救治伤员,并通知单位和人力资源部门。 单位申报:单位应在规定时间内(通常是30日内)向人社局提交《工伤认定申请表》及相关证据(如病历、证人证言、事故证明)。 认定:人社局调查后出具《工伤认定决定书》。 待遇:认定后,医疗费用由工伤保险基金报销,停工留薪期间工资照发,并根据伤残等级获得一次性伤残补助金等。

(2)生育保险

  • 核心好处:报销生育相关的医疗费(产检、分娩等),并领取产假期间的“工资”(叫生育津贴)。
  • 怎么享受报销:在定点医院生育,通常也是直接刷社保卡结算。 领津贴:女职工休产假期间,由生育保险基金支付生育津贴。一般是由单位代为申领,然后发放给个人。金额与单位上年度平均工资和产假天数挂钩。

最后,关于“一金”——住房公积金,它的好处极其实在:

  • 贷款:用公积金贷款买房,利率远低于商业贷款(能省下几十万利息)。
  • 提取:不仅可以用来买房、建房、租房,现在很多城市也支持用于支付大病医疗、老旧小区加装电梯等。

总结一下

“五险一金”不是虚无缥缈的概念,它是一套设计精密的国家保障体系。了解这些细节,才能在未来需要时真正地“用对”、“用好”它,让它成为您生活最坚实的后盾。建议您下载当地人社APP和公积金APP,实名认证后,可以随时查询自己的缴费明细和账户余额,做到心中有数。


1
2
3
4
5
6
领多久:与您缴费时间挂钩。
缴费满1年不足5年:最长领12个月。
缴费满5年不足10年:最长领18个月。
缴费10年以上:最长领24个月。

这个还是没有说明白,能领多少钱你说明白了,但具体能领多久你没说明白,假设我缴费至少一年了,我每次失业后都能领失业金吗,能领多久?再工作半年后又失业了还能继续领吗?到底能领多长时间。

您这个问题问到了关键点上!我之前的解释确实不够精确,非常抱歉。这个“最长领多久”的规则很容易让人混淆,因为它涉及到 “累计缴费年限”“单次领取期限”以及 “剩余待遇”这几个概念。

下面我用最清晰的方式为您彻底讲明白。

核心原则:不是“每次失业都能完整地领一遍”。

失业金的领取规则,可以理解为您有一个 “失业保险待遇账户”,这个账户的总额度是由您职业生涯总的累计缴费年限决定的。但每次失业,您只领取其中的一部分,没用完的额度可以存起来下次再用。


第一部分:首次失业,能领多久?(单次领取期限)

这个“最长领取月数”的表格,指的是您第一次符合条件申请失业金时,根据您截至当时为止的总缴费年限,所能领取的最长月数

举例说明:

  • 小王,工作了3年零1个月(累计缴费满3年,不足5年),不幸被公司裁员。 他首次失业,最多可以领取12个月的失业金。
  • 老李,在同一家公司勤勤恳恳工作了11年(累计缴费满10年以上),不幸被裁员。 他首次失业,最多可以领取24个月的失业金。

这就是您看到的那个表格的意思。它确定的是您“首次申领”时的天花板。


第二部分:最关键的问题——再就业后又失业,还能领吗?能领多久?

答案是:能领,但规则很特别。这里分两种情况,我们用两个例子来彻底讲清:

情况一:领完失业金后再就业,又失业

规则:重新就业后,再次缴费满一年以上后再次失业的,缴费时间重新计算,可以再次申领失业金。

  • 举例: 小王第一次失业,领了8个月失业金后找到新工作。 他在新单位又干了2年后,再次被裁员。 他能领多久?因为他重新就业后缴费已满一年(实际是2年),所以可以再次申领。根据他新的累计缴费年限(2年,属于“满1年不足5年”),他第二次失业最多可以再领12个月

注意:他第一次没领完的4个月(12-8=4)不会累积到第二次,因为第二次申领是基于新的缴费记录重新计算资格和期限的。

情况二:没领完失业金就再就业,又失业(您问的情况)

规则:这是最复杂也最重要的一条。前次失业应领取而尚未领取的失业保险金的期限可以保留,与下次失业后应领取的期限合并计算,但合并后的期限最长不能超过24个月。

  • 举例: 小王第一次失业,根据他7年的总缴费年限(满5年不足10年),他最多可以领18个月。 但他只领了3个月,就找到新工作了。于是他主动停止领取失业金。 他在新单位只干了半年(注意,这次缴费未满一年),又不幸被裁员。 他还能领吗?能领多久? 能领!因为他上次失业还有未领完的期限:18个月 - 3个月 = 15个月的额度。 这次他虽然只工作了半年(缴费未满一年),不具备“重新计算”的资格,但他可以继续申领上次剩余的15个月额度。 他这次失业,可以继续把上次剩下的15个月领完。

为了让您一目了然,我们可以把失业金想象成一个有时间限制的“专属红包”:

您的工作变动情况 对失业金的影响 关键点
第一次失业 根据总缴费年限,开启一个专属红包,有效期最长为表格规定的月数(如12、18、24个月)。 确定初始额度。
领完红包后再就业,又失业 必须重新工作并缴费满一年,才能开启一个新的红包。新红包大小由新的累计缴费年限决定。 重新计算,旧额度作废
没领完红包就再就业,又失业 旧红包里剩下的钱不会消失!你可以继续领完。如果新工作缴费又满一年,旧红包的剩余额度会和新红包的额度合并,但总额不能超过24个月。 余额保留,合并计算

最后的重要提醒

  1. 非自愿离职:必须是公司辞退、合同到期公司不续签等非本人意愿中断就业的情况才能申领。自己主动辞职是不可以的。
  2. 及时办理:离职后应在规定时间内(通常是60天)办理失业登记,才有资格领取。

希望这个详细的解释和例子能彻底解决您的疑惑!这套规则设计得比较精细,就是为了在保障失业者生活的同时,鼓励大家积极再就业。


1
我明白了,那能领多少钱跟自己缴费多少没有关系吗,我只看到你说按当地最低工资标准来发放。

您这个问题非常尖锐,直接触及了失业保险制度的一个核心特点:它更侧重于“保基本生活”而非“按劳分配”。

您的观察非常准——失业金的金额,确实与您失业前工资的高低、个人缴费的多少没有直接的正比关系。

但这不代表完全无关,我来为您拆解这其中的逻辑:

  1. 核心决定因素:确实是“最低工资标准”

您说得对,失业保险金的主要发放标准是参照参保地所在城市的最低工资标准的一个比例(通常是70%-90%)。这是为了确保一个公平的“托底”机制,保障所有失业者都能获得维持基本生活的费用,而不是拉大收入差距。

  • 例如:北京2023年月最低工资标准是2320元,那么失业金可能在1624元(2320×70%)到2088元(2320×90%)这个区间内。一个失业前月薪5000元和一个失业前月薪15000元的人,只要参保地相同,他们领取的失业金金额会非常接近。
  1. 间接关联因素:您的缴费工资有“天花板”和“地板”

虽然金额不直接挂钩,但您的缴费基数(也就是您的工资)通过以下方式间接影响着您能领到的钱:

  • “天花板”效应(封顶):失业金不会无限高。它有上限,这个上限就是当地最低工资的某个比例(如90%)。即使您月薪10万,缴费基数按最高上限缴纳,您领到的失业金也不会超过这个法定上限。
  • “地板”效应(托底):法律规定,您的缴费基数不能低于当地社保缴费的最低基数(通常是当地平均工资的60%或当地最低工资标准)。所以,即使您工资再低,也是按这个最低基数来缴费的,从而保证了您失业后领取的金额不会低于一个底线。

简单比喻:

失业保险更像一个“公共游泳池”,它有统一的浅水区和深水区(水位由最低工资决定),而不是一个根据您缴费量为您定制的“私人浴缸”。无论您之前贡献的水量是1吨还是10吨,您进来后都在同一个池子里游泳,水位对大家都一样。

  1. 与缴费直接挂钩的是什么?——是“领取资格”和“期限”

这才是您的缴费贡献真正发挥作用的地方!您的缴费工资高、缴费年限长,主要换来的是:

  1. 更稳固的领取资格:缴费满一年是门槛。
  2. 更长的领取期限:如我们之前讨论的,缴费5年能领18个月,缴费10年以上能领24个月。这是对长期、稳定缴费人员的重要回报。

为什么这样设计?

这种“金额保基本,期限论贡献”的设计,是基于社会保险的“共济”和“再分配”原则:

  • 社会共济:高收入者多缴费,与低收入者一起充实失业保险基金,共同增强社会抵御失业风险的能力。
  • 保障基本:核心目标是防止失业者及其家庭因收入中断而陷入贫困,而不是维持其原有的生活水平。维持原有生活水平更多需要通过个人储蓄、投资等渠道来实现。
  • 促进再就业:如果失业金与原有工资挂钩过高,可能会降低部分人寻找新工作的积极性。设定一个“保基本”的水平,有助于激励失业人员尽快重返劳动力市场。

总结一下:

您可以这样理解失业保险的逻辑:

  • “您能领多久”-> 这是对您长期缴费贡献的奖励。
  • “您能领多少”-> 这是社会对您基本生活需求的保障

所以,虽然您感觉自己的高缴费没有直接换来更高的失业金,但它为您换来了更长的保障期限和更稳固的保障资格,这本身就是一种重要的对等回报。希望这个解释能帮您更好地理解失业保险的设计初衷!

月薪

2025 年 11 月 6 日

这个计算是完全正确的,它准确地反映了在特定情况下(只工作了一段时间且处于试用期)的月工资计算方法。

我来为您详细解释一下这个公式背后的逻辑和每个数字的含义。

公式分解:8 / 21.75 * 4000 = 1471.26

这个公式是标准的 “日薪制”计算方法,适用于计算未出满勤月份的工资。

  1. 21.75(月计薪天数):这是整个计算的核心基础。它不是一个估算值,而是中国人力资源和社会保障部规定的官方计薪天数。来源:一年有365天,共52周零1天。扣除104天周末(52周 * 2天)后,剩下的261天为法定工作日。再将这261天平均到12个月,就得出:261天 ÷ 12个月 = 21.75天/月关键点:这个数字是固定的,不考虑当月实际有多少个工作日(比如2月可能只有20个工作日,而10月可能有23个)。所有月的计薪天数都统一按21.75天计算,这样可以避免大小月、节假日造成的计算不公平。
  2. 4000(您的试用期月薪):这是您与公司约定的、在正常工作满一个月(即计薪天数21.75天)的情况下,应得的全额工资。
  3. 8(您上个月的实际出勤天数):这是您在上个月实际为公司工作的天数。

计算逻辑(正算法):

  • 第一步:计算日薪

    1
    日薪=21.75月薪=21.754000183.91
  • 第二步:计算当月应得工资

    1
    应得工资=日薪×实际出勤天数=183.91×81471.28

整个计算的思路是:先算出您每天的工资是多少,然后再乘以您实际工作的天数。

服务器

2025 年 11 月 6 日

小型公司一般用什么服务器?这五点须知晓

1
2
3
4
.入门级塔式服务器
代表型号:戴尔PowerEdge T系列(如T150/T350)、惠普ProLiant ML系列(如ML30 Gen10+)、联想ThinkSystem ST系列(如ST250)。
使用场景:适合员工规模20人以下的企业,用于文件共享、基础ERP系统或小型数据库。
配置建议:通常配备Intel Xeon E-2300系列或AMD EPYC 3000系列处理器,16-32GB内存,1-2块企业级SATA硬盘(可选RAID 1冗余)。

公司服务架构_互联网公司服务器内存多大-CSDN博客

服务器的配置与公司架构的大小、使用场景、负载需求密切相关。以下是各类服务器配置总结:

小型公司:适合 4 核 CPU,8GB 内存,500GB SSD 的服务器,用于开发、测试及低流量生产环境。

中型公司:适合 8 核 CPU,16GB - 64GB 内存,1TB SSD 配 RAID 配置,用于高流量应用的生产环境。

大型公司:适合 16 核以上 CPU,64GB - 128GB 内存,企业级存储系统和高可用架构,用于高并发、大数据、高可用需求。

云架构:根据需求动态配置,使用弹性计算、存储和负载均衡服务,适合大规模分布式系统。

1
磁盘阵列模式(RAID)是一种通过组合多个独立硬盘实现数据冗余与性能优化的存储技术,全称为独立磁盘冗余阵列。

一篇文章彻底搞懂RAID磁盘阵列及其配置使用-CSDN博客

RAID(Redundant Array of Independent Disks)即独立磁盘冗余阵列,是一种通过多磁盘组合实现数据冗余、性能提升或二者兼顾的存储虚拟化技术。其核心原理包括:

  • 条带化(Striping):数据分块并行写入多磁盘(如RAID 0)
  • 镜像(Mirroring):数据完全复制到多磁盘(如RAID 1)
  • 奇偶校验(Parity):通过校验数据实现故障恢复(如RAID 5/6)

RAID的介绍和配置_组raid-CSDN博客

服务器尺寸详解:从1U到42U的完整指南

中国银行

2025 年 11 月 6 日

看看这周末有空出门办理下银行卡吧,这些事情其实上周出差那会儿就能告诉我的,工资打不到账户了才通知我要办个中国银行的卡。

中国银行开卡攻略:跑了三次终于搞定

中国银行怎么办银行卡-百度经验

身份证,在职证明?申请表,线上办理,设置交易密码,绑定信息,激活。

中国银行自助办卡流程是什么_百度知道

中国银行线上申请办卡指南

能线上办理才最好。

中国银行卡怎么办理 需要带什么过去_百度知道

(99+ 封私信 / 82 条消息) 中国银行卡办理 - 搜索结果 - 知乎

知乎上办理流程风评很差劲。

办银行卡是对私还是对公 - 财梯网

1
我要在中国银行APP上预约办理银行卡,单位发工资需要用中国银行卡,这里取号时选择现金业务还是其他业务啊,不懂。

应该选择“其他业务”。

原因如下:

  1. 业务性质匹配:办理银行卡(特别是工资卡)属于“账户开立”或“个人业务”,而不是直接的现金存取、兑换等“现金业务”。银行通常将“开卡”归类在“个人非现金业务”或简单的“其他业务”中。
  2. 界面提示:在APP的选项里,“现金业务”通常有明确的指向性。选择“其他业务”是一个更通用和准确的选择,可以涵盖开卡、挂失、签约网银等多种服务。
1
单位发工资要给中国银行卡里打钱,办理这种业务是对公还是对私的,去的时候需要携带什么证件?

为了确保一次办成,请按以下清单准备:

物品类别 具体物品 重要性 备注
必须携带 本人身份证原件 ★★★★★ 无身份证无法办理
必须携带 本人实名认证的手机 ★★★★★ 用于接收验证码和绑定银行卡
建议携带 工作证明(盖章文件/工牌/劳动合同) ★★★★☆ 证明办卡用途,成功率大增
需要知晓 个人详细现住址(精确到门牌号) ★★★☆☆ 银行系统必填项
需要知晓 单位全称、地址等信息 ★★☆☆☆ 开户时可能会询问并录入系统
1
我只能说办理一张工资卡吗,就不能找个合适的理由正常办理储蓄卡吗。

“你好,我想新开一张借记卡,作为工资卡使用。单位要求用中国银行的卡。”

直接说办“工资卡”其实是最合适、最顺利的理由。

为什么这么说?我们来对比一下:

说法 银行人员的理解 可能的结果 推荐指数
“我要办一张工资卡,单位发薪用。” “真实、合理的开卡需求。”工资卡是刚需,受反诈法规保护,银行认为风险较低。 流程清晰,目标明确。大堂经理会直接引导你走“工资卡”开户流程,即使需要你提供工作证明(如工牌),也是标准程序,并非刁难。 ★★★★★
“我就办张普通的储蓄卡,存点钱。” “用途模糊,可能存在风险。”在当前“断卡行动”背景下,这种无具体用途的开卡会触发银行更严格的风控审核。 可能会被详细追问。柜员或智能机具可能会问得更细:“主要用途是什么?”“预计资金流量多大?”“有没有理财需求?”等。回答得含糊其辞反而更可疑。

等周六上午去最近那家中国银行一趟,携带身份证和工作证明看能不能一次性办好,最近差旅费报销还有从下个月打工资都要用。

盘外

2025 年 12 月 2 日

Configuring a publishing source for your GitHub Pages site - GitHub Docs

(99+ 封私信 / 82 条消息) 什么是github-page,为啥好多人在这个上面部署网站,在这上面搭建是不需要自己有后台服务器吗? - 知乎

2025 年 12 月 1 日

微信小游戏包体限制4M,一个字体就11.24M,怎么玩?引言 哈喽大家好,很多时候,我们的游戏项目为了美观和保证风格的统 - 掘金

(99+ 封私信 / 82 条消息) 码云gitee的Pages服务下线了,有那些替代品? - 知乎

2025 年 11 月 21 日

Vue-Vben-Admin 从入门到实战:后端开发的前端探索之旅

2025 年 11 月 18 日

颜色网站为啥都收费?自己做个要花多少钱?如果一个人做个小型的视频网站,一年到底要花多少钱? 网站本身的成本+人力成本,我 - 掘金

刚刚 Java 25 炸裂发布!让 Java 再次伟大刚刚,Java 25 正式发布!这是继 Java 21 之后,又一 - 掘金

尤雨溪力荐!Vue3 生态最强大的 14 个 UI 组件库!在 Vue3 官网 的「Ecosystem → UI Com - 掘金

分享10个吊炸天的油猴脚本,2025最新!油猴(Tampermonkey)是一款浏览器插件,允许用户在网页加载时注入自定 - 掘金

我为什么说全栈正在杀死前端?大家好,我又来了🤣。 打开2025年的招聘软件,十个资深前端岗位,有八个在JD(职位描述) - 掘金

Java web后端转Java游戏后端作为Java后端开发者转向游戏后端开发,虽然核心编程能力相通,但游戏开发在架构设计 - 掘金

1
通过以上流程,Java后端开发者可逐步掌握游戏开发特性,重点需要转变的思维模式包括:从请求响应模式到实时状态同步、从CRUD主导到复杂逻辑计算、从分钟级延迟到毫秒级响应的要求。建议从简单的棋牌类游戏入手,逐步过渡到大型实时游戏开发。

没设计也能很漂亮,非常适合独立开发:Trae + 飞个马MCP 因为团队规模比较小,没有专门的设计师,就尝试使用Trae - 掘金

2025 年 11 月 14 日

我发现很多程序员都不会打日志。。。日志用来记录程序运行时的状态和信息,这样当系统出现问题时,我们可以通过日志快速定位问题 - 掘金

Logstash 负责收集各个服务的日志,然后发送给 Elasticsearch 存储和索引,最后通过 Kibana 提供一个可视化的界面。

让老弟做个数据同步,结果踩了 7 个大坑!第一天上班,老板就交给你一个艰巨的任务:定期把公司的订单数据同步到数据分析仓库 - 掘金

2025 年 11 月 10 日

那些年我们一起追过的Java技术,现在真的别再追了!今天的Spring Cloud、Kotlin、GraalVM,可能就 - 掘金

2025 年 11 月 5 日

最近想把这些系列给看了。

菜鸟教程 - 学的不仅是技术,更是梦想!

Docker入门到实践(一)docker简介和安装docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及 - 掘金

uni-app官网

竹子爱熊猫 的个人主页 - 专栏 - 掘金

Electron开发实践(1)——为什么选择ElectronElectron的诞生 Electron(原名为Atom S - 掘金

把 Vue.js 再过一遍,基础要打牢。

2025 年 11 月 6 日

Electron开发实践(4)——跨进程通信前言 进程间通信 (IPC) 是在 Electron 中构建功能丰富的桌面应 - 掘金

看不太进去。。

2025 年 11 月 7 日

接私活神器!一个轻量级的 Java 快速开发平台!X-SpringBoot —— 一个轻量级的 Java 快速开发平台, - 掘金

有了免费的Kiro,这次真的可以把Cursor扔了!Claude的金主爸爸亚马逊(AWS)偷偷发布了一款AI编程工具,K - 掘金

掘金,Docker。

AI时代的工作和成长引言 去年夏天,我一口气读完几篇AI文章,兴奋得连夜想写点什么。 可一年过去,键盘上落的不是代码,是 - 掘金

网线

2025 年 11 月 17 日

1
PL2303芯片 和 FTDI 芯片有什么区别?
特性 FTDI (Future Technology Devices International) Prolific (巨量)
核心定位 工业级、商用级。强调稳定性、可靠性和长期支持。 消费级、成本敏感型。主打高性价比和基本功能。
稳定性与性能 极高。公认的稳定性标杆,数据传输错误率低,兼容性极佳。 一般。在简单应用下没问题,但在复杂环境或长时通信中可能不如FTDI稳定。
驱动程序 “教科书级别”的稳定。驱动更新积极,对不同操作系统(包括旧版Win、Linux、甚至嵌入式系统)支持非常好。策略是持续优化兼容性 驱动是“重灾区”。为了打击山寨芯片,Prolific通过更新驱动故意让旧版和山寨芯片无法使用,导致用户常因驱动问题无法通信。
价格 较贵。芯片本身和采用FTDI芯片的线缆价格都更高。 便宜。极具成本优势,是大量廉价线缆的首选。
山寨情况 有,但FTDI的反制措施更“狠”(曾通过驱动将山寨芯片的PID/VID篡改,导致其变砖),一定程度上抑制了山寨泛滥。 山寨仿冒芯片极多。您买到的线,里面的PL2303芯片很可能就是山寨版本。
常见型号 FT232RL, FT231X, FT234X等 PL2303TA, PL2303HXD, PL2303HXS, PL2303GT

2025 年 11 月 5 日

高清线和hdmi线的区别是什么_装修全知道_学堂_齐家网

全网最强讲解:3000多字,解释清楚为什么需要HDMI线?怎么选?

G307/太原绕城高速(路口) - 百度地图

网线_百度百科

8路千兆以太网接口。

千兆网线8根线定义图_电脑与电脑之间的网线直连-CSDN博客

网络线序与标准详解:千兆网与百兆网差异深入解析

一条能通车的千兆网络,需要以下组成部分协同工作:

组成部分 实物比喻 作用和要求
1. 网卡(NIC) 汽车的引擎 安装在电脑、路由器等设备里。它是核心,决定了设备有没有能力跑千兆。一个百兆的网卡,即使用千兆网线和千兆路由器,速度也上不去。
2. 网线 公路路面 负责传输电信号。千兆网络至少需要使用 Cat 5e(超五类)或更高级的 Cat 6(六类)网线。路面的质量(线材好坏)直接影响稳定性和速度上限。
3. 路由器/交换机 交通枢纽/立交桥 负责指挥数据包去往正确的设备。你必须有一个千兆端口的路由器/交换机。如果路由器是百兆的,那么整个内部网络的速度就会被限制在百兆。
4. 接口(RJ-45) 公路的进出口匝道 就是你平时插网线的那个插槽和水晶头。它有8个金属触点。百兆网络实际上只用了其中4个(1,2,3,6),而千兆网络必须8个触点全部正常工作

简单自检:要想实现千兆速度,必须确保 网卡 + 网线 + 路由器三者都支持千兆标准,缺一不可。

它不是一种特殊的线,而是一个“质量标准”

首先纠正一个常见误解:并没有一种官方叫做“千兆网线”的线缆。这个称呼指的是达到了能够稳定传输千兆网络信号标准的网线

这就像说“能跑F1赛车的赛道”,它指的是一种赛道的修建标准(平整度、摩擦力、弯道设计等),而不是赛道本身的名字。


千兆网线 vs. 百兆网线(公路的“车道”数量)

最根本的区别在于8根芯线是否全部工作

网络类型 比喻 线缆要求 常用线缆类别
百兆网络 (100M) 双向4车道公路 只使用8根线里的4根(1, 2, 3, 6)来传输数据。另外4根是闲置的。 Cat 5(五类线)就足够了。
千兆网络 (1000M) 双向8车道公路 8根线必须全部使用,而且技术更先进,每对线都能同时收发数据。 至少需要 Cat 5e(超五类),推荐 Cat 6(六类)

关键点:如果你用一条只有4根芯线能通的老旧或劣质网线(比如某些只有4芯的电话线冒充的网线),那么即使你的电脑和路由器是千兆的,速度也会被限制在百兆。因为“路面”只有4条车道,无法满足8车道同时通车的需求。


如何识别一条“千兆网线”?(看路面的“等级标号”)

最可靠的方法就是看网线外皮上印刷的文字

  1. 寻找“CAT”字样:**CAT 5eCAT 5E:这是超五类线,是千兆网络的入门标准。在100米距离内能稳定支持千兆网络。这是目前最常见、性价比最高的选择。CAT 6:这是六类线,标准更高。它内部通常带一个“十字骨架”(如下图),用来更好地隔离4对线,减少串扰。它不仅完美支持千兆(1000M),还能支持更高速的万兆(10G)网络(但距离较短,约55米)。未来几年内,这是更好的选择。****CAT 6A**(超六类)、CAT 7等:支持更高速率(如万兆到100米),一般家庭目前用不到,多见于数据中心。
  2. 看水晶头:虽然不绝对,但千兆网线的水晶头,8个金属触点必须是齐全的、镀金的,看起来做工更扎实。百兆网线有些劣质的水晶头可能触点材质很差,甚至只有4个触点。

千兆网线的内在要求:为什么Cat 5e以上才能当千兆网线?

要达到“千兆标准”,这条“路面”必须在以下几个方面达标:

  1. 材质纯度(路面材质):必须使用无氧铜,电阻小,信号损耗低。劣质线会用铜包铝、铜包铁,电阻大,信号衰减严重,轻则降速,重则断网。
  2. 双绞密度(车道的平整度):网线里的8根线是两两相绞成4对的。绞合的密度越高,抗电磁干扰的能力就越强。Cat 6线的绞合密度通常比Cat 5e更高。
  3. 屏蔽性能(公路的隔音墙):在电磁干扰强的环境(比如旁边有强电线),需要使用屏蔽线。屏蔽线外有一层金属箔或编织网,像隔音墙一样保护内部信号。家用一般用非屏蔽线(UTP)就够了。

总结与选购建议

线缆类型 最大频率 支持的最高速率(理想条件下) 推荐场景
Cat 5 100 MHz 100 Mbps(百兆) 已淘汰,不要购买。
Cat 5e 100 MHz 1000 Mbps(千兆) 家庭主流选择,性价比高,完全满足千兆需求。
Cat 6 250 MHz 1 Gbps(100米)/ 10 Gbps(55米) 追求品质和未来性的选择,抗干扰更好,为万兆留有余地。
Cat 6A/7 500 MHz+ 10 Gbps(100米) 家庭极客、NAS重度用户、小型企业。

有时候这 AI 的理解要比绝大多数网站内容靠谱些。

2025 年 11 月 7 日

1
2
3
现在要采购一条网线,预计下周就要去现场调试设备了,得用自己的电脑连接高速收费站的费用显示器,在车道处调试下,现在需要我买一条网线,用电脑连接费显等设备时,得额外用一个USB转接口把笔记本和设备连接起来。

现在的问题是,网线应该买什么规格的,USB转接口呢,你推荐一下,今下午下班后我就买京东快递,明天就回来了。

核心采购清单(今晚下单,明天送达)

物品名称 规格要求 推荐理由 京东关键词示例
1. 网线 超五类类或六类非屏蔽网线,长度建议 3米或5米(预留操作空间) 超五类(Cat5e)或六, 完全足以应对千兆网络,是性价比最高的选择。非屏蔽型号(UTP)柔软易用,完全满足现场调试环境。3-5米长度足以在车道旁灵活操作。 “六类网线 3米” “超五类网线 5米”
2. USB转网口转换器 USB Type-C转RJ45千兆网口转接头(务必确认接口与您笔记本匹配) 这是关键设备!绝大多数现代轻薄本已取消RJ45网口,必须靠它转换。千兆是标准配置,[基本知识] 务必选择品牌货以保证驱动兼容性和稳定性,避免“三无”产品。 “USB转网口转换器 千兆” “Type-C转RJ45”

请立刻检查您笔记本电脑的接口类型,是传统的USB-A口(扁长型)还是USB-C口(椭圆形)。据此选择对应的转换器!

1
我的笔记本是 Lenovo XiaoXinPro 16lHU 2021,你给看下如果要买转接口,应该买什么样接口类型的。

好的,我们来快速准确地确定您需要的转接口类型。

根据您的笔记本电脑型号 **联想小新Pro 16 2021款 (16lHU)**,我们可以直接锁定它的接口规格。

您需要购买 Type-C 转 RJ45 网口转换器

详细说明和选购建议:

  1. 1.接口确认:您2021款的小新Pro 16,机身上配备的是 USB Type-C 接口。这个接口是椭圆形的,并且支持充电、数据传输和视频输出。因此,您必须选择 USB-C 转 RJ45 的转换器。
  2. 2.关键参数:务必选择 千兆(1000Mbps) 型号。百兆(100M)的已经过时,无法满足现代网络设备调试的需求。
  3. 3.选购关键词(用于京东搜索): •Type-C 转网口USB-C 转 RJ45 千兆扩展坞 千兆网口(如果您未来还需要连接U盘或显示器,可以考虑功能更多的扩展坞,但目前仅转网口的小转接头更便宜便携)

您的最终采购清单(今晚京东下单):

物品 为 小新Pro 16 2021款 精准匹配的规格
USB转网口转换器 Type-C 转 RJ45 千兆网口转接头
网线 六类(Cat6)或超五类(Cat5e)非屏蔽网线,长度3-5米

现在就可以打开京东APP或网站,直接搜索 “Type-C 转千兆网卡”,选择一个销量高、评价好的品牌(如绿联、毕亚兹、飞利浦等),今天下单,明天就能送达。

确保这个转接头能在您的电脑上正常识别,是明天现场调试成功的第一步。祝顺利!

usb线转网口线-usb线转网口线厂家、品牌、图片、热帖-阿里巴巴

usb转换线网线-usb转换线网线厂家、品牌、图片、热帖-阿里巴巴

虚拟机

环境变量

2026 年 2 月 26 日

Linux 系统中添加或追加环境变量_linux添加环境变量-CSDN博客

一、临时新增环境变量(快速测试用)

这种方式只在当前终端会话生效,关闭终端或切换用户后就失效,适合临时测试:

1
2
3
4
5
6
7
8
9
10
# 语法:export 变量名=值
# 示例1:新增Java的HOME路径(指向龙芯版JDK)
export JAVA_HOME=/home/loongson/src/jdk-11.0.29

# 示例2:把JDK的bin目录加到PATH(优先执行)
export PATH=$JAVA_HOME/bin:$PATH

# 验证是否生效
echo $JAVA_HOME # 输出:/home/loongson/src/jdk-11.0.29
echo $PATH # 开头会显示 /home/loongson/src/jdk-11.0.29/bin

关键:$PATH 表示 “保留原来的 PATH 值”,$JAVA_HOME/bin:$PATH 是把新路径加到最前面,优先执行。

二、永久新增环境变量(推荐,分场景)

场景 1:仅给当前普通用户(loongson)生效

修改用户个人配置文件,只影响当前用户,不会干扰其他用户,是最推荐的方式:

编辑用户的 .bashrc 文件(bash shell 的核心配置文件):

1
2
3
4
# 用vi编辑(也可以用nano,新手更友好)
vi ~/.bashrc
# 或
nano ~/.bashrc

在文件末尾添加新增环境变量的命令(示例):

1
2
3
4
5
6
# 新增JAVA_HOME(龙芯版JDK路径)
export JAVA_HOME=/home/loongson/src/jdk-11.0.29
# 把JDK的bin目录加到PATH最前面
export PATH=$JAVA_HOME/bin:$PATH
# 新增其他变量(比如CLASSPATH,Java常用)
export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar

保存退出后,让配置立即生效:

1
source ~/.bashrc

验证:

1
2
echo $JAVA_HOME  # 能看到配置的路径
java -version # 能正常执行,无格式错误

场景 2:给系统所有用户(包括 root)生效

修改系统全局配置文件,所有用户登录后都会加载,适合需要统一环境的场景:

编辑 /etc/profile(需要 root 权限):

1
sudo vi /etc/profile

在文件末尾添加同样的环境变量配置:

1
2
3
4
# 全局生效的Java环境变量
export JAVA_HOME=/home/loongson/src/jdk-11.0.29
export PATH=$JAVA_HOME/bin:$PATH
export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar

保存退出后,让全局配置立即生效(普通用户和 root 都要执行):

1
2
3
4
5
# 普通用户
source /etc/profile
# root用户(切换root后执行)
su root
source /etc/profile

验证:切换任意用户,执行 echo $JAVA_HOME 都能看到配置的路径。

场景3:**修改 /etc/environment**(仅纯变量定义)

你之前看到的 /etc/environment 是系统基础环境变量文件,只支持 “变量 = 值” 的纯定义格式,不能写 export,也不能用 $ 引用其他变量

1
2
3
4
5
# 编辑/etc/environment(需要root)
sudo vi /etc/environment
# 新增内容示例(注意:不能写export,不能用$PATH)
JAVA_HOME="/home/loongson/src/jdk-11.0.29"
PATH="/home/loongson/src/jdk-11.0.29/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games"

生效:需要重新登录(注销再登录),或重启系统。

  • 注意:这个文件优先级低,会被 .bashrc//etc/profile 覆盖,一般不推荐在这里改 PATH。

三、修改后验证 & 回滚

验证环境变量:

1
2
3
4
5
6
# 查看单个变量
echo $JAVA_HOME
# 查看所有环境变量(过滤关键词)
env | grep -i java
# 验证Java是否正常
java -version

回滚(如果配置错了):

  • 个人配置:删除 .bashrc 末尾添加的内容,执行 source ~/.bashrc
  • 全局配置:删除 /etc/profile 末尾添加的内容,执行 source /etc/profile

总结

  1. 临时新增:用 export 变量=值,仅当前 shell 生效;
  2. 永久新增(推荐):普通用户改 ~/.bashrc,所有用户改 /etc/profile,添加后执行 source 文件名 立即生效;
  3. 关键注意:/etc/environment 不支持 export 和变量引用,优先级低,一般不用来改 PATH;新增 PATH 时要保留原有值(加 $PATH),避免覆盖系统默认路径。

2025 年 12 月 18 日

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
[admin@localhost ~]$ ifconfig
docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.255
inet6 fe80::42:b1ff:fe3a:4b6e prefixlen 64 scopeid 0x20<link>
ether 02:42:b1:3a:4b:6e txqueuelen 0 (Ethernet)
RX packets 37 bytes 2060 (2.0 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 39 bytes 4066 (3.9 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.139.128 netmask 255.255.255.0 broadcast 192.168.139.255
inet6 fe80::b250:523d:d564:e75a prefixlen 64 scopeid 0x20<link>
ether 00:0c:29:b3:1a:75 txqueuelen 1000 (Ethernet)
RX packets 58 bytes 5312 (5.1 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 127 bytes 12725 (12.4 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1 (Local Loopback)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

veth5dbcacb: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet6 fe80::9003:99ff:fe82:3e26 prefixlen 64 scopeid 0x20<link>
ether 92:03:99:82:3e:26 txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 31 bytes 3292 (3.2 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

vethab9d652: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet6 fe80::c8c:94ff:fe14:38be prefixlen 64 scopeid 0x20<link>
ether 0e:8c:94:14:38:be txqueuelen 0 (Ethernet)
RX packets 37 bytes 2578 (2.5 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 42 bytes 3838 (3.7 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

virbr0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
inet 192.168.122.1 netmask 255.255.255.0 broadcast 192.168.122.255
ether 52:54:00:d1:4f:a7 txqueuelen 1000 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
C:\WINDOWS\system32>ipconfig

Windows IP 配置


无线局域网适配器 本地连接* 9:

媒体状态 . . . . . . . . . . . . : 媒体已断开连接
连接特定的 DNS 后缀 . . . . . . . :

以太网适配器 VMware Network Adapter VMnet1:

连接特定的 DNS 后缀 . . . . . . . :
本地链接 IPv6 地址. . . . . . . . : fe80::bcc3:1f75:3cb:b22f%11
IPv4 地址 . . . . . . . . . . . . : 192.168.91.1
子网掩码 . . . . . . . . . . . . : 255.255.255.0
默认网关. . . . . . . . . . . . . :

以太网适配器 以太网:

媒体状态 . . . . . . . . . . . . : 媒体已断开连接
连接特定的 DNS 后缀 . . . . . . . :

以太网适配器 VMware Network Adapter VMnet8:

连接特定的 DNS 后缀 . . . . . . . :
本地链接 IPv6 地址. . . . . . . . : fe80::52b0:a529:27d4:73d6%8
IPv4 地址 . . . . . . . . . . . . : 192.168.139.1
子网掩码 . . . . . . . . . . . . : 255.255.255.0
默认网关. . . . . . . . . . . . . :

无线局域网适配器 WLAN:

连接特定的 DNS 后缀 . . . . . . . :
IPv6 地址 . . . . . . . . . . . . : 2409:8744:0:17:0:1:0:28e
本地链接 IPv6 地址. . . . . . . . : fe80::515e:31f0:dafb:1bc3%2
IPv4 地址 . . . . . . . . . . . . : 172.16.20.211
子网掩码 . . . . . . . . . . . . : 255.255.254.0
默认网关. . . . . . . . . . . . . : fe80::5ab3:8fff:feba:6f19%2
172.16.20.1

隧道适配器 Teredo Tunneling Pseudo-Interface:

连接特定的 DNS 后缀 . . . . . . . :
IPv6 地址 . . . . . . . . . . . . : 2001:0:14c9:d502:3478:3413:8a5f:426
本地链接 IPv6 地址. . . . . . . . : fe80::3478:3413:8a5f:426%14
默认网关. . . . . . . . . . . . . :

2025 年 11 月 10 日

查看虚拟机磁盘空间大小。

1
dh

linux命令:查看空间(内存、磁盘、文件目录、分区) - 子非鱼灬亦非我 - 博客园

Linux使用du和sort命令查找最大文件和目录_Linux_脚本之家

1
clear

Linux的清屏命令(linux终端清屏)-腾讯云开发者社区-腾讯云

2025 年 11 月 5 日

vmware workstation pro 17激活码_vmware workstation17密钥-CSDN博客

1
You do not have permission to enter a license key. Try again using the system administrator account.

虚拟机VMware17安装包免费下载及安装激活教程(附激活密钥) - 影武浮云 - 博客园

最新的许可证密钥:

1
2
3
4
NZ4RR-FTK5H-H81C1-Q30QH-1V2LA
4A4RR-813DK-M81A9-4U35H-06KND
MC60H-DWHD5-H80U9-6V85M-8280D
JU090-6039P-08409-8J0QH-2YR7F

问题解决:

vmware 16无权输入密钥,联系管理员权限解决办法_you do not have permission to enter a license key.-CSDN博客

  1. 工具栏搜索“cmd”找到命令提示符;
  2. 右击以管理员身份运行;
  3. 找到VMware安装目录,进入VMware的安装目录下的 x64 文件夹,复制地址。如下:

image-20251105151423148

在已打开的cmd中输入代码:

1
2
3
4
5
cd /d d:

cd D:\Program Files\XUNIJI\x64(这里以我的目录为例,将加粗部分改为你的VMware的安装目录即可)

vmware-vmx.exe --new-sn xxxx-xxxx-xxxx-xxxx(密钥内容:YF390-0HF8P-M81RQ-2DXQE-M2UT6)

注意:cmd要以管理员身份运行,否则会导致命令无效。

1
2
3
4
5
cd /d d:

cd D:\dev\VMware Workstation Pro 17.0 64位

vmware-vmx.exe --new-sn NZ4RR-FTK5H-H81C1-Q30QH-1V2LA
1
vmware.exe --new-sn NZ4RR-FTK5H-H81C1-Q30QH-1V2LA
1
vmware-vmx.exe --new-sn xxxx-xxxx-xxxx-xxxx(密钥内容:YF390-0HF8P-M81RQ-2DXQE-M2UT6)

image-20251105151908271

测试完毕,执行这三条命令即可:

1
cd /d d:
1
cd D:\dev\VMware Workstation Pro 17.0 64位\x64
1
vmware-vmx.exe --new-sn NZ4RR-FTK5H-H81C1-Q30QH-1V2LA

顺利执行,不过没什么用,不起作用。

VMWare 17 虚拟机安装。

本机 CentOS7 密码为 :12345678。

1
12345678

边缘网关

2025 年 11 月 25 日

路段版本,顶部菜单。

架舱,设备检测,详情界面 -> 资产基本信息,监测信息,告警信息,资产拓扑图(省二期),性能指标,指标排序。

收费站监测,数据传输总览,数据传输指标,交易数据完整性。

系统公告,运维管理,统计分析(车道门架运行监测排行榜),导出报表功能,版本监测。

基础管理,设备管理,资产管理。

2025 年 10 月 30 日

RSU_百度百科

MTC(人工与自动化设备协同运作的高速公路收费系统)_百度百科

RS232/RS485串口:RSU 天线控制器,MTC 读卡机,移动支付扫码枪,费额显示器,称重设备。

千兆以太网接口:车牌识别摄像机,车型识别,智能栏杆机,打印机,特情机。

1
以太网供电(Power over Ethernet,简称PoE)也称PoE供电,是一种可以在以太网中透过双绞线来传输电力与数据到设备上的技术。
1
IC卡 (Integrated Circuit Card,集成电路卡),也称智能卡(Smart card)、智慧卡(Intelligent card)、微电路卡(Microcircuit card)或微芯片卡等。
1
CPC卡的英文全称Compound Pass Car(复合通行卡),主要用于高速公路收费系统中记录车辆行驶路径和计费信息。 ‌

收费站主要设备包括站级服务器、车道设备以及称重设备,应满足本章设备技术要求,同时应充分考虑国家安全战略要求及新技术发展和应用,实现联网收费系统安全、稳定、高效运行。

车道设备主要包括RSU、高清车牌图像识别设备、车型识别设备、电动栏杆机、车辆检测器、信息显示屏、手持终端、移动支付终端、自助发卡设备、 自助交费设备、车道控制器、非接触IC卡读写器等。若采用集成化设备,集成 的设备模块均应满足本章设备技术要求

1
2
PCI是Peripheral Component Interconnect(外设部件互连标准)的缩写,它是个人电脑中使用最为广泛的接口
PCL密码卡
1
PSAM(Purchase Secure Access Module)是销售点终端安全存取模块,符合中国金融集成电路(IC)卡规范,主要用于商用POS终端、网点终端等设备的安全控制管理,支持多级发卡机制和多应用环境。

出差

2026 年 2 月 3 日

1月5号,21.17,22.37;6号,19.77,38.79;7号,20.01,29.32;8号,20.9,52;9号,20,38.6;14号,20.01,31.98;15号,20.22,29.7;16号,20.65,31.17;20号,20.9,26.3;21号,21.65,35.29;22号,19.88,33.49;28号,19.6,42.29。

总计 656.06 元。

上个月去太原南打车花了 656.06 元。

2026 年 1 月 9 日

上个月在太原南打车220.92,买设备197.1,一共花销418.02块。

17号到4号,十九天。

在河南打车165,高铁470,住宿921.93,一共花销1556.93块。

2026 年 1 月 5 日

其实这段时间打车花销也挺大的,去太原南站打车9块,南阳东站到彭营打车花了 90 块,宾馆到中天打车花了22块,九里沟到济源站打车花了 37 块,南站到大马打车7块。

今天打车又花了 20 块,下午回去还当然得打车。

加班费算补贴,差旅费算报销,一码归一码。

2026 年 1 月 3 日

高铁票,太原到郑州 195.5 元,郑州东到南阳东 119 元,济源到焦作 12.5 元,焦作到太原 143 元。

高铁共计 470 元。

17号晚哈泊开开酒店 71 元,18 号晚幸福旅客驿站 100.77 元,19号 - 21 号合家欢宾馆 190 元,途客中国旅居酒店 480 元,今晚济源恒通宾馆 80.16 元。

住宿共计 921.93 元。

上个月总花销 470 + 921.93 + 418 = 1,809.93 元。

2026 年 1 月 2 日

差旅费不好算啊。

前期住酒店花销比较杂乱,吃饭完全没必要算,奔波了半个多月愣是没动手打过一次车。

那就是住酒店占大头,加上来回的高铁票,回去可能要打几趟车,到时候添上。

2025 年 12 月 21 日

周四回郑州东的地铁,当晚幸福率旅店住宿,吃一碗米线,周五一早去南阳的高铁票,打车去彭营,当晚邓州住一晚酒店,昨天下午吃一碗牛肉拉面。

2025 年 12 月 18 日

太原南到郑州的动车票,打车去酒店的花销,今早打车来中天的花销。

2025 年 12 月 12 日

一眨眼计算差旅费花销都已经是将近两周前的事情了。

电源适配器 45.90 元,一串四口线 151.20 元,插排暂时不算。

打车,上周五来回分别花销 19 元,45.6 元;上周六来回分别花销 22.42 元,22.2 元;这周一来回分别花销 19 元,44.26 元;周五来回分别花销 20.81 元,27.63 元。

买设备花销 197.1 元,打车花销 220.92 元,总花销 418.02 元。

周一,上个月的差旅费总花销给打到卡上了,高兴高兴。

2025 年 11 月 30 日

这周的花销。

千兆交换机,34.90 元;网线转换头,14.90 元;两条网线,17.43 元;公对母串口线延长线,35.90 元。

买设备一共花了 103.13 元。

周一没去,周二来回两趟车,分别花销 21.06 元,25.25 元;周三来回两趟车,分别花销 21.63 元,29.25 元;周四来回两趟车,分别花销 21.09 元,25.33 元;周五来回两趟车,分别花销 23.38 元,40.16 元;周六来回两趟车,分别花销 22.60 元,25.72 元。

打车一共花了 255.47 元。

截至今天,买设备一共花了 103.13 元 + 270.58 元 = 373.71 元,打车一共花了 164.08 元 + 255.47 元 = 419.55 元。

这个月开销差旅费开销,793.26 元

2025 年 11 月 24 日

上周的花销还没算进去。

额外买了两根新串口线,各自花销 62.10 元,62.10元。

周一来回打了两趟车,分别花销 16.85 元 ,28.38 元;周三,周四,周五中午打了一趟车过去,各自花销 23.02 元,22.22 元,22.54 元;周六来回打了两趟车,分别花销 25.01 元,26.06 元。

串口屏 93.25 元,USB 转 TTL 模块 53.13 元。

直至今天,买设备花销 270.58 元,打车花销 164.08 元 ,共计花销 434.66 元。

妈的,项目预备金早都超支了,剩下的开始自己垫付了都,多付了 51.41 元。

2025 年 11 月 18 日

周三,截至目前,又买了两条串口线,打了两趟车,今天还得打两趟车,还买了一个串口屏,老贵了这个东西。

今下午结束后总结下花销。

2025 年 11 月 16 日

统计下过去两周多时间里的花销。

网线 16.71 块,网线-USB转接线 49.00 块,键盘 25.90 块,串口线 59.00 块,打印两张车牌 2 块。

打车费,周六来回打了两趟车,周日来回打了两趟车,花销分别是 21.37 元,29.76 元,21.37 元,24.29 元。

直到今晚,买设备花销 152.61 元,打车花销 96.79 元,共计花销 249.4 元。

得算清楚,算明白。

当时两千块的预备金还剩 632.65 元,现在还剩 383.25 元。

2025 年 11 月 6 日

高铁536元,打车175.46元,住宿655.89元,吃饭192.8元,共计1,560.15元。

1
23号晚,郑州二七鸿运宾馆,花销69.84元,未开票。
1
这个没算进去。

住宿,高铁和打车的发票都开了。

替公司省钱,姜总额外补给自己300块。

公司现在的出差补助是50一天,包住宿、交通,吃饭自理,一共出差从24号到4号共12天,1,560.15 +600 - 192.8=1967.35。

我算一算。

项目预备金姜总当时给我打了两千块。

出差12天,出差每天补助50一共补助给我六百块,这是我应得的,拿到了。

替公司省钱,姜总额外补助给自己三百块,拿到了。

出差费用减去吃饭自理的花销,这是项目预备金真正开销的费用,一共是 1560.15 元 - 192.8 元 = 1,367.35 元。

这样算下来,自己一共拿了九百块补助,两千块的预备金还剩下 2000 元 - 1367.35 元 = 632.65 元

这六百多就是可支配的项目预备金了。

2025 年 11 月 5 日

截至今天,差旅费花销又新增了几项。

3号晚,下班后高德打车去酒店,花销13.91元;晚上点美团外卖,花销16.3元;品苑清雅酒店住宿花销99.83元,开票九十九块。

4号,上午高德打车去濮阳东站,花销27.24元。

4号,下午一顿汤面花了十块钱,如果这顿饭也算差旅费的话,花销10元。

2025 年 11 月 3 日

梳理下通勤费。

24号,东站打计程车到濮阳高速路口,花销52.60元,开票六十块。

25号,退了品苑清雅酒店后打车到爱尚依家宾馆,花销10.48元。

26号,中午打车去品苑清雅酒店开住宿发票,来回花销分别为13.31元,11.75元。

27号,28号,30号,31号,11月3号,早上打车到高速路口,花销分别为7.77元,8.36元,7.06元,7.18元,7.2元。

29号,早上打计程车到高速路口,花销8.6元,开票十块。

长治东-郑州,郑州东-濮阳东,濮阳东-郑州东,郑州东-太原,花销分别为112.5元,93元,116元,214.5元,暂未开票。

梳理下住宿费。

23号晚,郑州二七鸿运宾馆,花销69.84元,未开票。

24号晚,品苑清雅酒店,花销100.89元,开票一百块。

25-30号共六晚,爱尚依家宾馆,花销210元,开票二百块。

31-3号共三晚,朝夕相处宾馆,花销245.17元,暂未开票。

截至3号午,通勤 + 住宿共计1296.21元。

梳理下吃饭花了多少钱。

24号午,一碗西红柿鸡蛋面,花销14元;24号晚,美团点外卖,花销15元;25号-3号,冯记面馆吃了八碗面,共计花销64元,砂锅店光顾四次,共计花销52元,跑过两次小卖部,共计花销20元,共享单车骑过一次,花销1.5元。

截至3号午,通勤 + 住宿共计1462.71元。

四点半,写完今天的工作日志。

1
2
3
4
5
11月3日
现场观摩:自动栏杆机在交易成功后短暂失灵;
车道出卡机未检测到车辆吐卡失败,由维护人员手动按卡机绿色按钮后正常出卡;
卡机刷子损坏读卡不稳定,短暂关闭该车道后维护处理;
车道内车辆停靠不当,交易完成启动车辆后车侧身剐蹭到收费岛防护栏。

2025 年 10 月 31 日

1
C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images

微信图片_20251030203845

微信图片_20251030203905

微信图片_20251030203908

微信图片_20251030203911

微信图片_20251030203913

  1. 车辆驶入:压到地感线圈-> 线圈产生信号 -> 信号线传到黑色智能节点DI1端子 -> 节点通过网线告知白色网关-> 网关启动交易流程。
  2. ETC交易:网关通知RSU控制器-> 控制器通过射频线驱动RSU天线扫描 -> 与车载OBU通信 -> 交易数据经网关WAN口上传云端验证。
  3. 放行:验证成功 -> 网关通过网线命令智能节点-> 节点通过DO1端子控制栏杆机抬杆,通过DO2端子控制信号灯变绿 -> 同时,网关通过LAN口将金额发送到费额显示器显示。
  4. 车辆离开:压到出口线圈 -> 信号传至智能节点 -> 节点控制栏杆落杆。

image-20251031101654181

早上刚到就用 DeepSeek 分析收费岛核心网口布线逻辑。

贴一下这段解析吧。

图2 & 图3:机柜内部核心布局(对应地图中的白色网关、黑色节点)

这两张图展示的是同一个机柜内部,角度略有不同,我们结合起来看。机柜布局从上到下非常清晰:

  1. 顶层:电源接入与分配区(图2顶部)****你看到的是:一排电源插座,上面插着多个黑色电源适配器。接线逻辑:这是整个机柜的“心脏”。市电进来后,通过这些插座和适配器,转换成各种设备需要的安全电压(如12V、24V),再通过下方的红线(火线)、蓝线(零线)分配到各个断路器和设备。“冰箱”那个标签可能是个趣称,可能指代某个大功率设备或就是个内部标记。
  2. 中层:网络通信核心 —— 白色边缘网关(图3中部)****你看到的是:那个白色的、有多个网口和指示灯的设备。这就是我上次说的“智慧边缘网关”,是整个车道的“大脑”,负责数据交换和上传。**网口分布与接线逻辑(对照地图看,这是重点!):WAN/UPLINK口(图3,网关左侧的网口):这个口通常有显著不同。在你图片里,它很可能就是那个连接着蓝色网线或者最粗网线的接口。这是通往收费站服务器的总上行线,是车道的生命线,绝对不能动!****LAN口(图3,网关右侧的多个网口):这些口用于连接内部设备。根据常见配置:其中一个LAN口(比如LAN1)会接一根网线(比如灰色)到下层黑色“智能节点”(地图中的内部通信)。这就是“大脑”和“手脚”之间的神经。另一个LAN口(比如LAN2)会接一根网线到图4中的RSU控制器上。还会有一个LAN口预留给费额显示器**(图1/图5中的蓝色收费亭设备),网线会从机柜接到亭子里。
  3. 下层:现场控制核心 —— 黑色“智能节点”(图2/3下部)****你看到的是:那个标有“智能节点”的黑色设备,下面有一排排接线端子(绿色和黄色),插满了红、黄、蓝等颜色的电线。接线逻辑(对照地图看,这是难点!):****上行通信:它通过一根从上面白色网关来的网线(很可能是灰色)接收指令。数字量输入(DI):端子排上标有DI1DI2等字样的地方(图片中标签较小,但逻辑如此),接的是地感线圈的信号线。线圈检测到车辆,就给这里一个“通/断”信号。数字量输出(DO):端子排上标有DO1DO2等字样的地方,接的是自动栏杆机车道信号灯的控制线。系统命令通过这里发出,控制栏杆抬起落下,信号灯红绿变化。

图4:RSU控制器与配电区(对应地图中的RSU控制器)

这张图展示了机柜的另一个部分,通常是并排安装的。

  1. 顶部:配电断路器****你看到的是:两排“CHNT”品牌的微型断路器。接线逻辑:每个断路器控制一个回路。比如:一路给白色网关,一路给智能节点,一路给RSU控制器,一路给栏杆机,一路给费额显示器。你维护时操作某个设备,最好关闭对应的断路器,保证安全。
  2. 中部:RSU控制器****你看到的是:两个黑色的“RSU控制器”,上面有指示灯。这就是ETC通信的核心。接线逻辑(对照地图看):****网络端:它通过一根网线(比如从白色网关的LAN2口来的蓝色网线)与“大脑”连接,传输交易数据。射频端:它背后有专用的射频接口,通过一根粗大的同轴电缆连接到车道顶棚或安全岛上的RSU天线(就是那个像平板一样的东西)。车辆OBU的信息就是通过这个链路收发。
  3. 右侧:其他通信模块****你看到的是:灰色的模块,可能用于4G/5G备份通信或与收费站的其他系统交互。

十点半去路口把接线看了一遍,杨工不懂,附近也没什么人懂,这网口接线本来就是出厂后由专业人员配置的,各司其职哪有外行闲着没事干去瞎琢磨。

在这里工作也是够闲的,看他们的工作氛围就能明显觉察到,幸福感挺高。

中午十二点多就走了。

妈的,下午六点,工作日志还没写。

1
2
3
1031
1、现场观摩特情处理,车辆完成交易后电动栏杆未抬起;无牌车辆通行处理;卡机出卡卡死;一辆ETC车在出口车道交易时显示余额不足,引导司机将ETC卡从OBU上取下后,交由维护人员使用车道非接触IC卡读写器完成刷卡支付。
2、观察出口车道收费设备内部的布线逻辑,重点确认了读卡机、RSU天线、自动栏杆机和费额显示器设备的连接和通信状态,拍照保存。

写完咯,连同设备图也打包好给发过去,周末愉快!

2025 年 10 月 30 日

提早写好工作日志。

1
2
3
1030
1、现场观摩特殊情况处理:发卡机卡住没有正常出卡,快速断电重启设备后恢复正常;在ETC车道排队等候车辆的车载单元被旁边车道的RSU天线误读导致在本车道交易失败,引导该车辆倒车脱离感应区后重新驶入后,成功完成交易并抬杆放行;入口处后车跟车过近误将前车的通行卡取走,现场人员引导后车在安全区域等了10分钟,待系统释放掉该卡的绑定信息后再重新进车道取卡。
2、查看高速联网收费系统技术方案文档,系统学习车道主要设备技术要求、标志标线和交通安全措施等。

下午六点钟,刚吃完饭上楼进房间,打了两把游戏就看到姜总打来语音,明天得去高速路口拍设备接线和外形照片,回太原会提前通知。

2025 年 10 月 29 日

午饭前看完《交通运输部-高速公路联网收费系统优化升级收费站标准化专项试点技术方案附件》。

下午三点钟,昨天这个时候已经去高速路口收费站了。

下班前,写完今天的工作日志。

1
2
3
1029日:
1、现场观摩调整车道卡机处摄像头的监控角度,更准确捕捉车辆驶入和通行过程中的CPC卡取放操作。
2、学习车道设备与收费系统之间如何通过标准化的通信协议进行控制和数据交换的接口规范。

GitCode - 全球开发者的开源社区,开源代码托管平台

2025 年 10 月 28 日

今早问了通信机房有没有可供连接的设备,没有。

杨师傅刚给发了机器人和外场的日志,可以看过车的逻辑和交易的逻辑。

下午三点钟跟杨工去高速收费站处,在路口一直呆到四点半左右才返回上楼,观摩并记录了一些现场车辆通行出入口的特殊情况。

1
划防盗栏,卡机刷子,无卡过人工通道,卡太厚出不来,调整摄像头角度,出卡慢问题,无车牌处理,称重信息缺失,牵引车强制查验,排查摄像头问题,识别不到车辆,手动按绿色按钮弹出卡机

下班前工作日志写个大概:

1
2
3
4
5
1028
1、现场观摩车辆在收费站出入口通行特情处理,包括无车牌人工处理、卡机刷子损坏导致读卡不稳定、CPC卡太厚卡在出口出不来、货车剐蹭防护栏等。
2、观察到导致卡机出卡慢有诸多原因,如卡盒内CPC卡排列不齐、卡片过厚、初始化卡片信息时网络拥堵、感应线圈触发信号延迟等。
3、查看车道系统平台的日志区域了解车辆通行和缴费交易的逻辑。
4、查看高速联网收费系统技术方案文档,系统学习出入口车道关键业务流程、站级交易系统功能模块、车道布局等。

高速收费站入口处,卡机出卡慢会是什么原因导致的,扫描到车辆信息后需要做什么比对才能绑定什么信息来着,你给讲一讲。

DeepSeek 回答挺详细专业,学习一下:

好的,没问题!您这个问题问得非常专业,是高速公路收费系统运维中很常见的一个问题。我来给您详细拆解一下。

卡机出卡慢的常见原因

卡机(自动发卡机)出卡慢,就像打印机卡纸一样,是由多种因素造成的。我们可以从硬件、软件、外部环境三个层面来分析:

1. 硬件问题(最常见)

  • 卡盒问题:卡盒没有安装到位,或者卡盒内的通行卡数量太少、排列不整齐,导致取卡机械臂无法顺利吸起或抓取卡片。
  • 机械结构磨损/故障:发卡机的核心部件是取卡臂、传送轮等机械装置。长期使用后,可能会出现磨损、松动、或者有灰尘、油污堆积,导致动作不顺畅、卡顿。
  • 传感器故障:发卡机内部有多个光学或机械传感器,用于检测卡盒是否有卡、卡片是否被成功抓取、卡片是否运送到出卡口等。任何一个传感器失灵或脏污,都会导致系统误判,从而反复尝试或停止出卡流程,造成延迟。
  • 卡片本身问题:通行卡变形、弯曲、过厚或过薄,也会在传送过程中遇到阻力。

2. 软件与系统问题

  • 系统响应延迟:当车辆驶入触发线圈后,触发信号需要经过工控机上传到收费站级的服务器,甚至可能要与省中心的系统进行短暂的通信,以获取时间、站号等信息来初始化一张卡片。如果网络拥堵或服务器处理能力达到瓶颈,这个响应过程就会变慢。
  • 软件BUG或死机:发卡机的控制软件可能出现临时性的错误,需要“反应”一下,或者工控机本身运行了太多程序,导致资源不足。

3. 外部环境因素

  • 触发线圈灵敏度:埋设在车道下的感应线圈可能因为损坏或参数漂移,导致检测车辆不灵敏,触发信号延迟,从而让整个出卡流程的启动就慢了半拍。
  • 天气因素:在极端的潮湿、寒冷或炎热天气下,电子元件和机械结构的性能可能会受到影响。

简单总结一下处理思路:如果遇到某台卡机出卡慢,运维人员通常会先检查卡盒和卡片,然后清洁传感器和传送路径,最后再排查软件日志和网络连接。


扫描车辆信息后的比对与绑定流程

您问的这个问题,正是目前高速公路收费系统从 “计重收费”“车型收费” 转变的核心,主要应用于ETC和CPC卡(复合通行卡)的入口流程。

当车辆驶入车道,摄像头(以及可能的其他辅助设备)会扫描捕捉车辆信息,主要包括:车牌号码、车牌颜色、车辆外观(用于AI识别车型)。

系统捕捉到这些信息后,会进行一系列快速的比对和绑定,以确保后续计费的准确性。

需要比对的信息清单:

  1. 与部级/省级数据库比对(核心比对) 比对对象:全国ETC客户服务系统或省级数据库中的车辆档案。 比对内容车牌号:识别出的车牌号是否真实有效。 车牌颜色:蓝牌、黄牌、绿牌等,不同颜色代表不同的车辆类型(如客车、货车、新能源车等)。 车辆特征(车型分类):这是最关键的一步。系统会用AI图像识别技术判断车辆的实际物理特征(如车轴数、车头高度、轮廓等),并与数据库中该车牌登记的标准车型进行比对。 目的:防止“大车小标”(例如,实际是三类货车,但办理ETC时登记为二类车,以少交通行费)。如果识别出的车型与数据库登记的车型不一致,系统会标记异常,在出口时可能会转为人工核查,按实际车型收费。
  2. 与黑名单/状态名单比对 比对对象:状态名单系统(包括黑名单、挂失卡、支付账户异常等名单)。 比对内容:该ETC车辆或CPC卡是否被列入异常名单。 目的:如果车辆在黑名单中,入口栏杆不会抬起,或者系统会提示工作人员进行干预。

绑定什么信息?

在完成上述比对且无误后,系统会生成一条入口流水记录,并将以下信息绑定在一起,写入即将发放的CPC卡ETC交易记录中:

  • 入口信息:入口收费站编号、入口时间、车道号、操作员号。
  • 车辆信息车牌号码识别出的车型(作为计费依据)。
  • 卡片信息:CPC卡的内部序列号。

简单来说,这个绑定的过程就是:“谁(车牌车型)”“在哪儿上的(入口站)”“什么时候上的(入口时间)”“用的哪张卡(卡号)” 这几条信息打包成一个数据包。

后续流程:

  • 对于ETC车辆,这个数据包会通过RSU(路侧天线)写入OBU(车载单元),然后抬杆放行。
  • 对于非ETC车辆(取CPC卡),这个数据包会写入CPC卡中,然后卡机才吐卡。司机拿到的CPC卡里,已经存储了所有这些信息。在出口时,读卡器会读取这些信息,结合出口信息,计算出准确的通行费。

2025 年 10 月 27 日

上网找找资料看。

高速公路收费系统工程应用软件技术方案-CSDN博客

下午,姜总补发了两份项目文档,可供参考学习。

《智能边缘网关产品研发技术方案V1.1》《交通运输部-高速公路联网收费系统优化升级收费站标准化专项试点技术方案附件》。

晚八点钟,补上今天的工作日志。

1
2
3
4
1027
1、杨师傅详细解答收费站入口处地感线圈如何在车辆驶过后,触发三目摄像头抓拍车牌和车型识别,并发出是否有车信号控制栏杆上抬下落的,介绍各环节如何衔接。
2、人工收费车道出现了车主准备取卡,而读卡机发生故障卡死没有正常吐出通行卡的问题,观摩了现场维护人员打开设备机柜快速重启硬件后让卡机出卡正常;此外杨师傅简单描述了无牌车、车牌识别错误等情况的现场处理流程。
3、学习智能边缘网关产品研发技术方案,初步了解高速公路收费系统的整体硬件架构、软件架构以及标准化设计原则。

2025 年 10 月 24 日

27号晚,补上24号的工作日志。

1
2
10月24日
跟杨师傅去到高速路口收费站现场,认识了ETC天线、读卡发卡机、车牌识别摄像机、自动栏杆机和费用显示器等外部核心设备,了解到来往车辆从入站识别、计费收费到抬杆放行的基本流程。

晚上住一晚酒店,接下来的两天周末可以好好休息了。

河南

特微智能

2025 年 12 月 18 日

trawe.cn

特微乐行(广州)技术有限公司_百度百科

硬件产品:发卡机器人,缴费机器人,ETC 特情自助终端,智慧服务厅,关道机 + 限高智能终端,岛头智能节点,岛尾智能节点,边缘云站,缴费 + 电票智能终端,智能支付终端,相控阵天线,读卡器,电子标签,智慧云舱。

软件产品:智慧云收费系统,远程值守云坐席系统,移动支付系统,云监控平台,电子发票系统,ETC 发行平台。

中天高科

2025 年 12 月 18 日

到郑州的第二天。

昨天下午四点半就到了郑州站,找个车子去预定的酒店呆了一晚,六点多点两份外卖把晚饭解决了,随便玩会儿晚十二点不到就睡觉了。

早上七点半醒来,洗漱收拾行李,下楼,在路口打个车子出发去中天,半小时后到,八点半就坐在工位上了。

二楼,挺宽敞一办公室。

分布式消息中间件-RocketMQ_rocketmq解耦-CSDN博客

百度地图

北京特微智能科技有限公司_百度百科

2025 年 10 月 27 日

在河南濮阳的第四天,上周五下午到了濮阳站高速路口,在附近度假区订了六晚的民宿一直呆到今天。

没什么好聊的,已经在日记里吐槽过了。

事业单位

医生

2026 年 2 月 25 日

医生要编制好还是不要编制好呢?

1
2
3
4
工作稳定性
事业编制医生:事业编制医生属于事业单位的正式员工,工作稳定性较高。编制内医生享有国家规定的各类福利保障,如医疗保险、养老保险、住房公积金等。此外,编制内医生的工作岗位相对固定,受到单位保护,较少面临被解雇的风险。

合同制医生:合同制医生通过签订劳动合同的方式受聘于医院,工作稳定性相对较低。合同期满后,医院有权决定是否续约,这使得合同制医生在工作稳定性上不如编制内医生。
1
2
3
事业编制医生:编制内医生的福利待遇通常较为优厚,包括稳定的工资、绩效奖金、带薪年假、定期体检等。此外,事业编制医生在退休后能够享受相对较高的养老金,这为其晚年生活提供了保障。

合同制医生:合同制医生的福利待遇因医院和地区而异,可能不如编制内医生稳定和丰厚。虽然一些大城市的大型医院可能为合同制医生提供较好的待遇,但总体来说,他们的福利水平仍不如事业编制医生。
1
2
3
事业编制医生:编制内医生在职业发展上可能享有更多的机会和资源。例如,他们通常有更多的机会参加专业培训、学术交流和科研项目。此外,事业编制医生在职称评审和晋升方面可能会受到优待。

合同制医生:合同制医生在职业发展上面临更多的挑战和不确定性。他们需要通过出色的工作表现和个人努力来获得职业晋升的机会。尽管合同制医生在某些情况下可能会受到不公平的待遇,但一些能力出众的医生依然能够在职业上取得显著成就。
1
2
3
事业编制医生:事业编制医生的职业路径相对固定,工作调动和变更较为困难。这种稳定性虽然提供了安全感,但也限制了职业灵活性。对于一些希望尝试不同医院或地区工作机会的医生来说,编制内的限制可能成为一种束缚。

合同制医生:合同制医生在职业选择上具有更大的灵活性。他们可以根据个人兴趣和职业规划自由选择不同的医院和工作地点。这种灵活性使得合同制医生能够更好地适应个人和职业发展的需求。
1
2
3
安全感和稳定性:对于重视工作稳定性和安全感的医生来说,事业编制无疑是一个更好的选择。编制内的稳定工作和优厚福利能够提供更高的生活保障,减少职业风险。

追求挑战和自由:对于那些喜欢挑战和追求自由的医生来说,合同制可能更为适合。他们可以利用灵活的职业选择来实现个人职业目标,获得更多的职业发展机会和生活体验。

编制

2026 年 3 月 5 日

专技岗晋升实行的是职称评聘制,分初、中、高三个层次13个等级,其中11-13级为初级职称,8-10级为中级职称,5-7级为副高职称,1-4级为正高职称。从初级职称晋升中级职称,原则上要求拿到初级职称并在岗三年以上,中级晋升副高,要求在中级岗位任满五年以上,副高晋升正高,同样要求副高满五年。

专技岗想要晋升一般要求具备三个条件:1、拿到相应的职称,这是先决条件;2、职称岗位有空缺,这是必要条件;3、单位和领导愿意聘任,这是决定条件。三个条件缺一不可。

(99+ 封私信 / 81 条消息) 山西省直事业单位考试容易考吗? - 知乎

教材,机构,题集,套卷。

中公,花果,粉笔,中公。

2026 年 3 月 3 日

事业单位考试A、B、C、D、E类是指什么_华图问答_广东华图教育

事业单位abcde类的区别是什么_事业单位招聘考试_华图教育

2026 年 2 月 25 日

事业单位管理岗与技术岗有哪些区别_华图教育

人事考试网 - 山西省人力资源和社会保障厅

专业技术岗、管理岗和工勤岗傻傻分不清

img

管理岗和专技岗有什么区别?这五个方面对比,就明白了!

卫生专业技术等级一般分为十三个等级,你都知道吗?

(99+ 封私信 / 81 条消息) 卫生专业技术人员各级别职称制度 - 知乎

2026 年 2 月 9 日

(99+ 封私信 / 81 条消息) 事业单位考试自然科学c类具体考哪些? - 知乎

2026 年 1 月 6 日

人事考试网 - 山西省人力资源和社会保障厅

2026山西事业编考试时间:3月28日、29日! - 上岸鸭公考

2026 年 1 月 26 日

驾考

2026 年 2 月 27 日

虚假一年,吊销两年,撤销三年,醉驾五年,逃逸终生不得再考(假一吊二撤三醉五逃终生)。

北京市小车科目一模拟考试章节练习【驾考宝典】

花了一小时,做完了驾驶证和机动车管理100题。

2026 年 2 月 26 日

科一、科二、科三、科四到底考什么?80%的学员都没搞清楚

第一次考驾照不知道考什么?一分钟看完流程_懂车帝

🚗手动挡VS自动挡,哪个更优?-有驾

2025驾校学车考驾照,到底选手动挡C1好还是选自动挡C2好?一篇文章讲清楚区别


保驾护航:后台运维视角下的高速公路“生命线”
http://example.com/2025/10/23/保驾护航:后台运维视角下的高速公路“生命线”/
作者
Memory
发布于
2025年10月23日
更新于
2026年3月5日
许可协议