- modem.go: DBus 属性的裸类型断言改为安全的 comma-ok 模式,避免 panic - messaging.go: Subscribe 中增加 sig 的 nil 检查,防止 channel 关闭时 panic - sms.go: 时区解析改用正则表达式,正确处理 +05:30 等半小时偏移时区 - manager.go: Modems() 中 NewModem 失败时记录警告日志 Co-authored-by: HoshinoAya <HoshinoDesu@users.noreply.github.com>
158 lines
3.4 KiB
Go
158 lines
3.4 KiB
Go
package modem
|
|
|
|
import (
|
|
"fmt"
|
|
"log/slog"
|
|
|
|
"github.com/godbus/dbus/v5"
|
|
)
|
|
|
|
const (
|
|
ModemManagerInterface = "org.freedesktop.ModemManager1"
|
|
ModemManagerPath = "/org/freedesktop/ModemManager1"
|
|
ObjectManagerInterface = "org.freedesktop.DBus.ObjectManager"
|
|
)
|
|
|
|
// Manager 调制解调器管理器
|
|
type Manager struct {
|
|
conn *dbus.Conn
|
|
dbusObject dbus.BusObject
|
|
}
|
|
|
|
// NewManager 创建管理器
|
|
func NewManager() (*Manager, error) {
|
|
conn, err := dbus.SystemBus()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("连接系统总线: %w", err)
|
|
}
|
|
|
|
obj := conn.Object(ModemManagerInterface, ModemManagerPath)
|
|
return &Manager{
|
|
conn: conn,
|
|
dbusObject: obj,
|
|
}, nil
|
|
}
|
|
|
|
// Modems 获取所有调制解调器
|
|
func (m *Manager) Modems() (map[dbus.ObjectPath]*Modem, error) {
|
|
// 使用 ObjectManager 接口获取所有管理的对象
|
|
managedObjects := make(map[dbus.ObjectPath]map[string]map[string]dbus.Variant)
|
|
err := m.dbusObject.Call(ObjectManagerInterface+".GetManagedObjects", 0).Store(&managedObjects)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("获取调制解调器列表: %w", err)
|
|
}
|
|
|
|
modems := make(map[dbus.ObjectPath]*Modem)
|
|
for objectPath, interfaces := range managedObjects {
|
|
// 检查对象是否包含 Modem 接口
|
|
if _, hasModem := interfaces[ModemInterface]; !hasModem {
|
|
continue
|
|
}
|
|
|
|
modem, err := NewModem(m.conn, objectPath)
|
|
if err != nil {
|
|
slog.Warn("初始化调制解调器失败", "path", objectPath, "error", err)
|
|
continue
|
|
}
|
|
modems[objectPath] = modem
|
|
}
|
|
|
|
return modems, nil
|
|
}
|
|
|
|
// Subscribe 订阅调制解调器事件
|
|
func (m *Manager) Subscribe(handler func(event ModemEvent) error) (func(), error) {
|
|
if err := m.conn.AddMatchSignal(
|
|
dbus.WithMatchInterface(ObjectManagerInterface),
|
|
dbus.WithMatchMember("InterfacesAdded"),
|
|
dbus.WithMatchPathNamespace(ModemManagerPath),
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := m.conn.AddMatchSignal(
|
|
dbus.WithMatchInterface(ObjectManagerInterface),
|
|
dbus.WithMatchMember("InterfacesRemoved"),
|
|
dbus.WithMatchPathNamespace(ModemManagerPath),
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
signalChan := make(chan *dbus.Signal, 10)
|
|
m.conn.Signal(signalChan)
|
|
done := make(chan struct{})
|
|
|
|
go func() {
|
|
for {
|
|
select {
|
|
case <-done:
|
|
return
|
|
case sig := <-signalChan:
|
|
if sig == nil || len(sig.Body) == 0 {
|
|
continue
|
|
}
|
|
|
|
path, ok := sig.Body[0].(dbus.ObjectPath)
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
var event ModemEvent
|
|
event.Path = path
|
|
|
|
switch sig.Name {
|
|
case ObjectManagerInterface + ".InterfacesAdded":
|
|
event.Type = ModemEventAdded
|
|
modem, err := NewModem(m.conn, event.Path)
|
|
if err == nil {
|
|
event.Modem = modem
|
|
}
|
|
case ObjectManagerInterface + ".InterfacesRemoved":
|
|
event.Type = ModemEventRemoved
|
|
default:
|
|
continue
|
|
}
|
|
|
|
if err := handler(event); err != nil {
|
|
slog.Debug("处理调制解调器事件失败", "error", err)
|
|
}
|
|
}
|
|
}
|
|
}()
|
|
|
|
unsubscribe := func() {
|
|
close(done)
|
|
m.conn.RemoveSignal(signalChan)
|
|
}
|
|
|
|
return unsubscribe, nil
|
|
}
|
|
|
|
// Conn 返回 D-Bus 连接
|
|
func (m *Manager) Conn() *dbus.Conn {
|
|
return m.conn
|
|
}
|
|
|
|
// Close 关闭 D-Bus 连接
|
|
func (m *Manager) Close() error {
|
|
if m.conn != nil {
|
|
return m.conn.Close()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ModemEventType 调制解调器事件类型
|
|
type ModemEventType int
|
|
|
|
const (
|
|
ModemEventAdded ModemEventType = iota
|
|
ModemEventRemoved
|
|
)
|
|
|
|
// ModemEvent 调制解调器事件
|
|
type ModemEvent struct {
|
|
Type ModemEventType
|
|
Path dbus.ObjectPath
|
|
Modem *Modem
|
|
}
|