Skip to content

fix(linkage): ClassIsland IPC 连接统一与联动刷新修正 - #285

Merged
lzy98276 merged 4 commits into
masterfrom
fix274-classisland-ipc
Sep 25, 2026
Merged

lzy98276 merged 4 commits into
masterfrom
fix274-classisland-ipc

Conversation

@lzy98276

Copy link
Copy Markdown
Member

在 @CreeperAWA 的 #284 基础上做了 rebase 与合入前修正,他的三个 commit 原样保留(作者仍是他本人),只在末尾追加一个维护者修复 commit。

原 PR 的 base 是 0737daf0,与当前 master 冲突,且它的 ClassIslandScheduleSource.cs 还停留在旧的 IsLessonConfirmed 门槛与 Breaking/_ => Unknown 映射上——直接合并会回退刚修好的 #278。这里已按当前 master 重新整合。

修复的问题

  1. 联动快照新鲜度(功能回归):RefreshAsync 在语义比较相等时直接 return,不再写入新快照,而课前解禁、课后禁用延迟、课前重置和刷新调度只从快照读倒计时(没有第二个时钟来源)。结果是课前解禁窗口不会按时打开、课前重置基本不会触发。现在改为「始终写入快照,只用语义比较决定是否通知订阅者」——疑似与ClassIsland联动出错 #274 的自激循环来自事件通知,不是来自快照写入。
  2. 恢复 SecRandom4Ci 插件版本门槛(1.2.0.0):原 PR 删掉了 GetPluginVersion() 校验,MinimumPluginVersion 成了死常量,旧插件会被当作可用。
  3. 不再每次空等 10 秒:GetLessonsServiceAsync 在退避期内立即返回;通知路径单独用 1 秒预算,避免内置回退通知与「抽取前就该打开」的 QuickDraw 结果窗口被拖后最多 10 秒。
  4. 恢复 IPC 调用超时:IsAlive() / ShowNotification() 重新包上 1 秒 WaitAsync,ClassIsland 卡住时发送路径不会一直挂着;失败改走 _logger(原来是 Debug.WriteLine,Release 下进不了 data/logs)。
  5. 清理死常量、修正「CurrentTimeStateChanged 每秒触发」这一不准确的注释(ClassIsland 只在状态变化时广播)、Dispose 不再释放仍在使用的信号量、PluginDrawServiceTests 补注册新连接类型(原 PR 会让 3 个既有测试失败)。

测试

新增/更新后全量 543 通过 / 0 失败,其中新增:

  • 倒计时变化但状态不变 → 不触发 StateChanged,但 Snapshot.TimeUntilNextCourse 必须更新
  • 课前解禁窗口按时打开(同样不触发 StateChanged)
  • IPC 退避期内 GetLessonsServiceAsync / GetNotificationServiceAsync 立即返回
  • 插件版本门槛(1.1.0.0 / null / 非 "Yes" 均判为不可用)
  • 原有的 v3.0.0-alpha.2 非上课时段禁用功能在时间表未覆盖时段无效 #278 映射与放学/开课前用例继续保留

fix #274

CreeperAWA and others added 4 commits September 25, 2026 20:00
1.  新增ClassIslandIpcConnection统一管理IPC连接,替换原分散在两个服务中的连接代码
2.  重构课程联动服务的快照比较逻辑,忽略倒计时等动态字段避免误触发状态变更
3.  调整课程联动刷新策略,仅在完全离线时轮询并延长轮询间隔
4.  统一通知服务的IPC连接逻辑,复用新的连接管理类
1. 为IPC连接断开添加500ms延迟重连,避免和ClassIsland的广播循环竞争
2. 新增对IPC连接断开异常的捕获处理,分别在存活检测和通知发送场景中静默处理断开情况并触发内置回退
重置下次连接尝试时间为最小值,当前重试延迟为最小延迟,允许立即重连,避免与 ClassIsland 的广播循环产生竞争。
在 #284 的基础上补齐合入前必须修掉的问题:

- 刷新时始终写入新快照,只用语义比较决定是否通知订阅者。完全丢弃倒计时会让
  课前解禁、课后禁用延迟、课前重置和刷新调度读到陈旧值(#274 的自激循环来自
  事件通知,不来自快照写入)
- 恢复 SecRandom4Ci 插件版本门槛(1.2.0.0),旧插件不再被当作可用
- 退避期内立即返回,不再每次空等 10 秒;通知路径只等 1 秒,避免内置回退通知
  与抽取前就该打开的 QuickDraw 结果窗口被拖后
- 恢复 IsAlive/ShowNotification 的 1 秒 IPC 调用超时,失败改走 _logger
- 清理死常量与不准确的注释,Dispose 不再释放仍在使用的信号量
- 补测试:倒计时变化不通知但快照必须更新、课前解禁窗口按时打开、退避期快速返回、
  插件版本门槛;PluginDrawServiceTests 注册新的连接类型

fix #274

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6bc046dbcb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +4 to +5
using ClassIsland.Shared.IPC;
using ClassIsland.Shared.IPC.Abstractions.Services;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep ClassIsland types inside the schedule adapter

This new shared connection class directly imports and exposes ClassIsland IPC types, even though the app-layer contract requires ClassIsland types to remain inside ClassIslandScheduleSource. Keep the ClassIsland-specific connection implementation in that adapter, or place a ClassIsland-free interface between it and consumers, so the linkage/notification services do not acquire a second ClassIsland integration boundary.

AGENTS.md reference: SecRandom/AGENTS.md:L166-L166

Useful? React with 👍 / 👎.

Comment on lines +144 to +146
// Only poll when completely unavailable - otherwise rely on events
if (Settings.DataSource == LinkageDataSource.ClassIsland && !_snapshot.IsAvailable)
return TimeSpan.FromSeconds(5);
return TimeSpan.FromMinutes(5);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Retry unavailable ClassIsland connections promptly

When SecRandom starts before ClassIsland, or ClassIsland has not yet restarted after a failed connect, the source is unavailable and this five-minute delay is used. No connected peer exists to raise StateChanged, so the hosted scheduler cannot wake early; confirmed break restrictions, floating-window hiding, and pre-class resets remain inactive for up to five minutes after ClassIsland becomes available. Keep a short retry interval while the ClassIsland source is unavailable, or have the connection manager schedule its own retry.

AGENTS.md reference: SecRandom/AGENTS.md:L166-L166

Useful? React with 👍 / 👎.

Comment on lines +20 to +23
private static readonly TimeSpan JsonRouteReadyDelay = TimeSpan.FromSeconds(1);
private static readonly TimeSpan IpcCallTimeout = TimeSpan.FromSeconds(1);
private static readonly TimeSpan LessonsWaitTimeout = TimeSpan.FromSeconds(10);
private static readonly TimeSpan NotificationWaitTimeout = TimeSpan.FromSeconds(1);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Allow the first external notification to finish connecting

With linkage disabled (the normal case), the first external draw is also the first IPC connection attempt. The notification caller waits only one second, while the connector necessarily spends the same one second on JsonRouteReadyDelay after Connect() plus probe work before publishing the service, so that first awaited ClassIsland animation always returns without sending. This drops the first external rolling animation and delays the draw; give the notification bootstrap a budget larger than the connection setup, or start the connection before the first draw.

AGENTS.md reference: SecRandom/AGENTS.md:L151-L151

Useful? React with 👍 / 👎.

Comment on lines +90 to +95
var service = NotificationService;
if (service is not null)
return service;

await WaitForLessonsServiceAsync(NotificationWaitTimeout, cancellationToken).ConfigureAwait(false);
return NotificationService;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reprobe a notification plugin that was unavailable initially

If the initial connection succeeds for lessons but SecRandom4Ci is still loading, times out during its first probe, or is enabled later, _notificationService is stored as null. Subsequent calls enter this method, immediately return from WaitForLessonsServiceAsync because lessons are cached, and return the same null notification service without ever attempting another plugin probe; external notifications stay disabled until the entire ClassIsland peer disconnects. Retry the optional notification proxy independently when it is absent.

AGENTS.md reference: SecRandom/AGENTS.md:L151-L151

Useful? React with 👍 / 👎.

@lzy98276
lzy98276 merged commit bea34df into master Sep 25, 2026
16 checks passed
@lzy98276
lzy98276 deleted the fix274-classisland-ipc branch September 25, 2026 12:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

疑似与ClassIsland联动出错

2 participants