package netmgrd import "C" import ( "sync" "sync/atomic" "syscall" "time" "github.com/vishvananda/netlink" modem "hnyfkj.com.cn/rtu/linux/air720u" "hnyfkj.com.cn/rtu/linux/baseapp" ) const MODULE_NAME = "NetworkManager" var ( isOnline atomic.Bool // 标记是否联网 offlineStartTs atomic.Int64 // 离线开始时间 isSyncNTPTimeOK atomic.Bool // 标记本地时间是否已同步成功 mu1 sync.Mutex isRunning1 bool exitCh1 chan struct{} wg1 sync.WaitGroup curNetType NetType ) type NetType int const ( NetNone NetType = iota NetEth // 有线网络 NetLTE // 蜂窝网络 ) const ( interval1 = time.Duration(1) * time.Second interval2 = time.Duration(5) * time.Second ) func ModuleInit() { go serviceRun() } // 联网保持服务 func serviceRun() { // 1, 首次连接网络 openNetwork() // 2, 监听有线插拔 eth0PlugCycleCh := make(chan bool, 1) done := make(chan struct{}) defer close(done) go monitorEth0PlugCycle(eth0PlugCycleCh, done) // 3, 循环监控网络 t := time.NewTimer(interval1) defer t.Stop() for { select { case <-t.C: // 3.1, 联网检测-看结果 dnsOK, pingOK, tcpOK, httpOK := CheckNetwork() baseapp.Logger.Infof("[%s] 联网检测: DNS OK=%v, PING OK=%v, TCP OK=%v, HTTP OK=%v", MODULE_NAME, dnsOK, pingOK, tcpOK, httpOK) // 3.2, 检测成功-在线时 if dnsOK && pingOK && tcpOK { isOnline.Store(true) offlineStartTs.Store(0) if isSyncNTPTimeOK.Load() { t.Reset(interval2) continue } err := SyncNTPTime() if err == nil { isSyncNTPTimeOK.Store(true) baseapp.Logger.Infof("[%s] NTP时间已同步", MODULE_NAME) t.Reset(interval2) continue } else { baseapp.Logger.Errorf("[%s] 同步NTP时间时有错误发生: %v!!", MODULE_NAME, err) t.Reset(interval1) continue } } // 3.3, 检测失败-离线时 if isOnline.Load() { // 状态由"1"变为"0" isOnline.Store(false) offlineStartTs.Store(time.Now().UnixNano()) // 记录离线开始时间 } if offlineDuration() >= (time.Duration(60) * time.Second) { baseapp.Logger.Warnf("[%s] 检测到网络长时间断开, 将重新尝试连接网络!", MODULE_NAME) openNetwork() offlineStartTs.Store(time.Now().UnixNano()) // 重置离线开始时间 } t.Reset(interval1) case <-eth0PlugCycleCh: // 有线网口插拔事件, 重新连接网络, 优先有线网 openNetwork() continue case <-baseapp.IsExit2(): return } // select end } // for end } // 返回联网状态 func IsInetAvailable() bool { return isOnline.Load() } // 时间是否同步 func IsSyncedNtpTime() bool { return isSyncNTPTimeOK.Load() } // 等待所有成功 func WaitAllOK(timeout time.Duration) bool { deadline := time.Now().Add(timeout) tick := 50 * time.Millisecond for { if IsInetAvailable() && IsSyncedNtpTime() { return true } remaining := time.Until(deadline) if remaining <= 0 { return false } sleep := min(tick, remaining) time.Sleep(sleep) } } // 返回断网时长 func offlineDuration() time.Duration { if IsInetAvailable() { return 0 } ts := offlineStartTs.Load() if ts == 0 { return 0 } return time.Since(time.Unix(0, ts)) } // 打开有线网络 func openEth0Net() bool { mu1.Lock() defer mu1.Unlock() if isRunning1 { return true } killAllUdhcpc() err := disableEthBut0() if err != nil { baseapp.Logger.Errorf("[%s] 错误: %v!!", MODULE_NAME, err) return false } bExists, _ := udhcpcEth0Exists() if bExists { killEth0Udhcpc() } err = dialupEth0() if err != nil { baseapp.Logger.Errorf("[%s] 拨号连接\"eth0\"时发生错误: %v!!", MODULE_NAME, err) return false } ipv4, mask, err := getEth0Addr() if err != nil { baseapp.Logger.Errorf("[%s] 读取\"eth0\"地址时发生错误: %v!!", MODULE_NAME, err) return false } baseapp.Logger.Infof("[%s] \"eth0\"分配的地址: %s/%s", MODULE_NAME, ipv4, mask) exitCh1 = make(chan struct{}) wg1.Add(1) go func() { // 启动携程守护"eth0"网卡上的 "udhcpc"后台服务进程 defer wg1.Done() monitorEth0Udhcpc(exitCh1) }() isRunning1 = true return isRunning1 } // 断开有线网络 func closeEth0Net() { mu1.Lock() defer mu1.Unlock() if !isRunning1 { return } close(exitCh1) wg1.Wait() // 等待守护"eth0"网卡上的 "udhcpc"后台服务进程的携程退出 bExists, _ := udhcpcEth0Exists() if bExists { killEth0Udhcpc() } isRunning1 = false } // 打开连接网络 func openNetwork() { closeEth0Net() modem.CloseEth2Net() eth0CableOK, _ := isEth0CableConnected() if eth0CableOK && curNetType != NetEth && openEth0Net() { curNetType = NetEth baseapp.Logger.Infof("[%s] ✅ 有线网络已连接", MODULE_NAME) return } tryOpenLTE := func() bool { if eth2CableOK, _ := modem.IsEth2CableConnected(); eth2CableOK { if modem.OpenEth2Net() { return true } // 重试: 4G模组重新上电初始化 modem.ModuleExit() modem.ModuleInit(true) return modem.OpenEth2Net() } return false } if tryOpenLTE() { curNetType = NetLTE enableEth0() // 保持 eth0 可用, 以便监听网线的插拔, 触发切换网络 baseapp.Logger.Infof("[%s] ✅ 蜂窝网络已连接", MODULE_NAME) return } curNetType = NetNone baseapp.Logger.Warnf("[%s] 没有可用的网络连接!", MODULE_NAME) } // 监控网线插拔 func monitorEth0PlugCycle(plugCycle chan bool, done chan struct{}) error { updates := make(chan netlink.LinkUpdate) if err := netlink.LinkSubscribe(updates, done); err != nil { return err } link, err := netlink.LinkByName("eth0") if err != nil { return err } isUp := link.Attrs().Flags&syscall.IFF_RUNNING != 0 waitingForPlug := false for u := range updates { if u.Link.Attrs().Name != "eth0" { continue } newUp := u.IfInfomsg.Flags&syscall.IFF_RUNNING != 0 if newUp == isUp { continue } isUp = newUp if !isUp { baseapp.Logger.Warnf("[%s] ❌ eth0 cable unplugged!", MODULE_NAME) waitingForPlug = true } else if waitingForPlug { baseapp.Logger.Infof("[%s] ✅ eth0 cable plugged in", MODULE_NAME) select { case plugCycle <- true: case <-done: return nil } waitingForPlug = false } } return nil } //export RTU_IsInetAvailable func RTU_IsInetAvailable() C.int { if IsInetAvailable() { return 1 } return 0 } //export RTU_IsSyncedNtpTime func RTU_IsSyncedNtpTime() C.int { if IsSyncedNtpTime() { return 1 } return 0 }