11package com .lokins .sleepy .gui .controller ;
22
3- import com .lokins .sleepy .gui .network .SleepyClient ;
3+ import com .lokins .sleepy .gui .network .ISleepyClient ;
4+ import com .lokins .sleepy .gui .network .SleepyClientFactory ;
5+ import com .lokins .sleepy .gui .network .SleepyClientV5 ;
6+ import com .lokins .sleepy .gui .network .SleepyPingProvider ;
47import com .lokins .sleepy .gui .service .MonitorService ;
58import com .lokins .sleepy .gui .utils .ConfigManager ;
69import javafx .application .Platform ;
7- import javafx .event .ActionEvent ;
810import javafx .fxml .FXML ;
911import javafx .scene .control .*;
1012import org .slf4j .Logger ;
1113import org .slf4j .LoggerFactory ;
12-
1314import java .io .IOException ;
1415
1516public class ConnectViewController {
1617 private static final Logger logger = LoggerFactory .getLogger (ConnectViewController .class );
1718
18- @ FXML private TextField serverUrlField ;
19- @ FXML private PasswordField secretField ;
20- @ FXML private TextField deviceNameField ;
21- @ FXML private Button startBtn ;
22- @ FXML private Label statusLabel ;
23- @ FXML private Button testConnBtn ; // 必须添加这一行
19+ @ FXML private TextField serverUrlField , deviceNameField ;
20+ @ FXML private PasswordField secretField , refreshTokenField ;
21+ @ FXML private Label statusLabel , refreshLabel ;
22+ @ FXML private Button startBtn , testConnBtn ;
23+ @ FXML private CheckBox autoProtocolCheck ;
24+ @ FXML private ChoiceBox < String > protocolVersionBox ;
2425
25- // 使用静态变量或单例,确保页面切换时服务不被销毁
2626 private static MonitorService monitorService ;
2727 private static boolean isRunning = false ;
2828
2929 @ FXML
3030 public void initialize () {
31- // 恢复之前的配置
32- try {
33- ConfigManager config = ConfigManager .getInstance ();
34- serverUrlField .setText (config .get ("server" , "url" , "" ));
35- secretField .setText (config .get ("auth" , "secret" , "" ));
36- deviceNameField .setText (config .get ("device" , "name" , "My-PC" ));
37- } catch (IOException e ) {
38- logger .error ("加载配置失败" , e );
39- }
40-
41- // 恢复按钮状态(防止从日志页切回来时按钮变回“开始”)
42- updateUIState ();
43- }
44-
45- @ FXML
46- private void handleStart () {
47- try {
48- ConfigManager config = ConfigManager .getInstance ();
49- config .set ("server" , "url" , serverUrlField .getText ());
50- config .set ("auth" , "secret" , secretField .getText ());
51- config .set ("device" , "name" , deviceNameField .getText ());
52-
53- // !!!必须加这一行,否则文件不会更新 !!!
54- config .save ();
55-
56- logger .info ("配置已持久化到磁盘" );
57- } catch (IOException e ) {
58- logger .error ("无法保存配置" , e );
31+ // 1. 初始化下拉框选项(仅执行一次)
32+ if (protocolVersionBox .getItems ().isEmpty ()) {
33+ protocolVersionBox .getItems ().addAll ("Protocol v5 (Legacy)" , "Protocol v6 (Modern)" );
5934 }
6035
61- if (!isRunning ) {
62- startMonitoring ();
63- } else {
64- stopMonitoring ();
65- }
66- }
36+ loadConfiguration ();
6737
68- private void startMonitoring () {
69- String url = serverUrlField .getText ();
70- String secret = secretField .getText ();
71- String device = deviceNameField .getText ();
72-
73- if (url .isEmpty () || secret .isEmpty () || device .isEmpty ()) {
74- logger .warn ("配置不完整,取消启动" );
75- showAlert (Alert .AlertType .ERROR , "启动失败" , "配置信息不完整。请确保服务器地址、密钥和设备名称都已填写。" );
76- return ;
38+ if (isRunning && monitorService != null ) {
39+ String currentVersion = monitorService .getClient ().getVersionTag (); // 需在 MonitorService 增加 getClient
40+ if (currentVersion .contains ("v6" )) {
41+ protocolVersionBox .setValue ("Protocol v6 (Modern)" );
42+ } else {
43+ protocolVersionBox .setValue ("Protocol v5 (Legacy)" );
44+ }
45+ updateUIState (); // 锁定所有输入框
7746 }
7847
79- // 启动逻辑
80- SleepyClient client = new SleepyClient (url , secret , device );
81- monitorService = new MonitorService (client , (appName ) -> {
82- Platform .runLater (() -> statusLabel .setText ("正在运行: " + appName ));
48+ // 4. 【核心修复】监听下拉框,改变即保存
49+ protocolVersionBox .getSelectionModel ().selectedItemProperty ().addListener ((obs , oldVal , newVal ) -> {
50+ if (newVal != null && !isRunning ) {
51+ updateRefreshFieldState (); // 更新刷新密钥置灰状态
52+ saveCurrentConfig (); // 只要改了就保存,防止切换 Tab 丢失
53+ }
8354 });
8455
85- monitorService .start ();
86- isRunning = true ;
87- updateUIState ();
88- logger .info (">>> 监控已启动,设备名: {}" , device );
89- }
90-
91- private void stopMonitoring () {
92- if (monitorService != null ) monitorService .stop ();
93- isRunning = false ;
94- updateUIState ();
95- logger .info (">>> 监控已手动停止" );
96- }
97-
98- private void updateUIState () {
9956 if (isRunning ) {
100- startBtn .setText ("停止监控" );
101- startBtn .setStyle ("-fx-background-color: #e74c3c; -fx-text-fill: white;" );
102- statusLabel .setText ("状态: 运行中" );
57+ statusLabel .setText ("状态: 正在监控 (" + protocolVersionBox .getValue () + ")" );
10358 } else {
104- startBtn .setText ("开始监控" );
105- startBtn .setStyle ("" ); // 恢复 CSS 默认
10659 statusLabel .setText ("状态: 已停止" );
10760 }
10861 }
10962
63+ private void updateRefreshFieldState () {
64+ boolean isV6 = protocolVersionBox .getValue ().contains ("v6" );
65+ boolean isAuto = autoProtocolCheck .isSelected ();
66+ // 只有非自动模式且选了 V6,或者自动模式(待检测)时,我们根据需要开启
67+ refreshTokenField .setDisable (!isV6 && !isAuto );
68+ refreshLabel .setOpacity (refreshTokenField .isDisable () ? 0.5 : 1.0 );
69+ }
70+
71+ @ FXML
72+ private void handleAutoProtocolToggle () {
73+ protocolVersionBox .setDisable (autoProtocolCheck .isSelected ());
74+ updateRefreshFieldState ();
75+ }
76+
11077 @ FXML
11178 private void handleTestConnection () {
11279 String url = serverUrlField .getText ().trim ();
113-
11480 if (url .isEmpty ()) {
115- showStatus ( "请输入服务器地址 " , "red " );
81+ showAlert ( Alert . AlertType . WARNING , "提醒 " , "请输入服务器地址后再进行测试 " );
11682 return ;
11783 }
11884
119- if (!url .startsWith ("http://" ) && !url .startsWith ("https://" )) {
120- url = "http://" + url ;
121- serverUrlField .setText (url );
122- }
123-
124- // 禁用按钮防止重复点击
85+ // UI 反馈
12586 testConnBtn .setDisable (true );
126- testConnBtn .setText ("测试中 ..." );
87+ statusLabel .setText ("状态: 正在测试连通性 ..." );
12788
128- String finalUrl1 = url ;
89+ // 在后台线程执行网络操作,避免 UI 卡死
12990 new Thread (() -> {
130- boolean success = false ;
131- try {
132- // 2. 使用锁定的 url 变量
133- SleepyClient testClient = new SleepyClient (finalUrl1 , "" , "" );
134- success = testClient .ping ();
135- } catch (Exception e ) {
136- System .err .println ("测试连接失败: " + e .getMessage ());
137- }
91+ SleepyPingProvider .PingResult result = SleepyPingProvider .testConnection (url );
13892
139- // 3. 回到主线程更新 UI
140- final boolean finalSuccess = success ;
93+ // 回到 UI 线程更新界面
14194 Platform .runLater (() -> {
14295 testConnBtn .setDisable (false );
143- testConnBtn . setText ( "测试连接" );
144-
145- if ( finalSuccess ) {
146- showAlert ( Alert . AlertType . INFORMATION , "成功" , "服务器连接正常! " );
96+ if ( result . success ) {
97+ statusLabel . setText ( "状态: 连接正常 (" + result . latencyMs + "ms)" );
98+ showAlert ( Alert . AlertType . INFORMATION , "测试通过" ,
99+ "成功连接到服务器! \n 响应延迟: " + result . latencyMs + "ms " );
147100 } else {
148- showAlert (Alert .AlertType .ERROR , "失败" , "无法连接到服务器,请检查地址或网络。" );
101+ statusLabel .setText ("状态: 连接失败" );
102+ showAlert (Alert .AlertType .ERROR , "测试失败" , result .message );
149103 }
150104 });
151105 }).start ();
152106 }
153107
154- private void showStatus (String message , String color ) {
155- System .out .println ("Status: " + message );
156- }
108+ @ FXML
109+ private void handleStart () {
110+ if (isRunning ) {
111+ stopMonitoring ();
112+ return ;
113+ }
157114
158- private void showAlert (Alert .AlertType type , String title , String content ) {
159- Alert alert = new Alert (type , content , ButtonType .OK );
160- alert .setTitle (title );
161- alert .setHeaderText (null );
162- alert .showAndWait ();
115+ saveCurrentConfig ();
116+
117+ if (autoProtocolCheck .isSelected ()) {
118+ statusLabel .setText ("状态: 自动识别中..." );
119+ new Thread (() -> {
120+ int version = SleepyClientV5 .probeProtocolVersion (serverUrlField .getText ());
121+ Platform .runLater (() -> executeStartSequence (version ));
122+ }).start ();
123+ } else {
124+ executeStartSequence (protocolVersionBox .getValue ().contains ("v6" ) ? 6 : 5 );
125+ }
163126 }
164127
165- @ FXML
166- private void handleSaveConfig () {
167- String url = serverUrlField .getText ().trim ();
168- String secret = secretField .getText ().trim ();
169- String deviceName = deviceNameField .getText ().trim ();
128+ private void executeStartSequence (int version ) {
129+ // 确保在主线程更新 UI 状态
130+ Platform .runLater (() -> {
131+ // 如果是自动检测出来的版本,我们需要手动同步下拉框的值
132+ // 这样 updateRefreshFieldState() 才能拿到正确的结果
133+ if (autoProtocolCheck .isSelected ()) {
134+ if (version == 6 ) {
135+ protocolVersionBox .setValue ("Protocol v6 (Modern)" );
136+ } else {
137+ protocolVersionBox .setValue ("Protocol v5 (Legacy)" );
138+ }
139+ }
140+ // 核心修复:强制刷新“刷新密钥”一栏的禁用/启用状态
141+ updateRefreshFieldState ();
142+ });
170143
171- if (url .isEmpty ()) {
172- showAlert (Alert .AlertType .WARNING , "校验失败" , "服务器地址不能为空" );
173- return ;
144+ // 原有的启动逻辑...
145+ ISleepyClient client = SleepyClientFactory .createClient ();
146+ monitorService = new MonitorService (client , (appName ) -> {
147+ Platform .runLater (() -> statusLabel .setText ("正在运行: " + appName ));
148+ });
149+
150+ if (monitorService .start ()) {
151+ isRunning = true ;
152+ Platform .runLater (this ::updateUIState ); // 务必在 UI 线程刷新按钮文字
153+ } else {
154+ Platform .runLater (() -> {
155+ statusLabel .setText ("状态: 连接失败" );
156+ isRunning = false ;
157+ updateUIState ();
158+ });
174159 }
160+ }
175161
162+ private void stopMonitoring () {
163+ if (monitorService != null ) monitorService .stop ();
164+ isRunning = false ;
165+ updateUIState ();
166+ }
167+
168+ @ FXML
169+ private void handleSaveAction () {
170+ saveCurrentConfig ();
171+ showAlert (Alert .AlertType .INFORMATION , "成功" , "配置已保存" );
172+ }
173+
174+ private void saveCurrentConfig () {
176175 try {
177176 ConfigManager config = ConfigManager .getInstance ();
178- // 写入配置内存
179- config .set ("connection " , "server_url " , url );
180- config .set ("connection " , "secret " , secret );
181- config .set ("connection " , "device_name " , deviceName );
182-
183- // 执行持久化,写入 .sleepy/config.ini
177+ config . set ( "server" , "url" , serverUrlField . getText ());
178+ config .set ("auth " , "secret " , secretField . getText () );
179+ config .set ("auth " , "refresh_token " , refreshTokenField . getText ()); // 存入 auth 下
180+ config .set ("device " , "name " , deviceNameField . getText () );
181+ config . set ( "settings" , "auto_protocol" , String . valueOf ( autoProtocolCheck . isSelected ()));
182+ config . set ( "settings" , "protocol_version" , autoProtocolCheck . isSelected () ? "0" : ( protocolVersionBox . getValue (). contains ( "v6" ) ? "6" : "5" ));
184183 config .save ();
184+ } catch (IOException e ) { logger .error ("保存失败" , e ); }
185+ }
185186
186- showAlert (Alert .AlertType .INFORMATION , "保存成功" , "连接配置已持久化到本地。" );
187- logger .info ("用户手动保存了连接配置: {}" , url );
188- } catch (IOException e ) {
189- logger .error ("保存配置失败" , e );
190- showAlert (Alert .AlertType .ERROR , "保存失败" , "无法写入配置文件: " + e .getMessage ());
191- }
187+ private void loadConfiguration () {
188+ try {
189+ ConfigManager config = ConfigManager .getInstance ();
190+ serverUrlField .setText (config .get ("server" , "url" , "" ));
191+ secretField .setText (config .get ("auth" , "secret" , "" ));
192+ refreshTokenField .setText (config .get ("auth" , "refresh_token" , "" ));
193+ deviceNameField .setText (config .get ("device" , "name" , "My-PC" ));
194+ boolean isAuto = Boolean .parseBoolean (config .get ("settings" , "auto_protocol" , "true" ));
195+ autoProtocolCheck .setSelected (isAuto );
196+ protocolVersionBox .setDisable (isAuto );
197+ updateRefreshFieldState ();
198+ } catch (Exception e ) { logger .warn ("配置加载失败" ); }
199+ }
200+
201+ private void updateUIState () {
202+ // 强制 UI 切换
203+ startBtn .setText (isRunning ? "停止监控" : "开始监控" );
204+ startBtn .getStyleClass ().removeAll ("primary-button" , "danger-button" );
205+ startBtn .getStyleClass ().add (isRunning ? "danger-button" : "primary-button" );
206+
207+ // 运行中禁用,停止后启用
208+ boolean disabled = isRunning ;
209+ serverUrlField .setDisable (disabled );
210+ secretField .setDisable (disabled );
211+ refreshTokenField .setDisable (disabled || !protocolVersionBox .getValue ().contains ("v6" )); // V6 特殊判断
212+ deviceNameField .setDisable (disabled );
213+ autoProtocolCheck .setDisable (disabled );
214+ protocolVersionBox .setDisable (disabled || autoProtocolCheck .isSelected ());
215+ testConnBtn .setDisable (disabled );
216+ }
217+ private void showAlert (Alert .AlertType type , String title , String content ) {
218+ Alert alert = new Alert (type , content , ButtonType .OK );
219+ alert .setTitle (title );
220+ alert .setHeaderText (null );
221+ alert .showAndWait ();
192222 }
193223}
0 commit comments