package netmgrd import "C" import ( "sync" "sync/atomic" "time" "hnyfkj.com.cn/rtu/linux/baseapp" modem1 "hnyfkj.com.cn/rtu/linux/air720u" // 合宙4G调制解调器 modem2 "hnyfkj.com.cn/rtu/linux/ec200u" // 移远4G调制解调器 ) const MODULE_NAME = "NetworkManager" var ( isOnline atomic.Bool // 标记是否联网 offlineStartTs atomic.Int64 // 离线开始时间 isSyncNTPTimeOK atomic.Bool // 标记本地时间是否已同步成功 lastSyncNTPTime time.Time // 记录同步网络时间成功的时间 curNetType NetType // 当前的网络类型: 有线、蜂窝 mu1 sync.Mutex isRunning1 bool exitCh1 chan struct{} wg1 sync.WaitGroup ) type NetType int const ( NetNone NetType = iota NetEth // 有线网络, 注册网卡名为"eth0" NetLTE // 蜂窝网络 ) func (n NetType) String() string { switch n { case NetEth: return "有线" case NetLTE: return "蜂窝" default: return "未知" } } const ( interval1 = time.Duration(1) * time.Second interval2 = time.Duration(5) * time.Second ) func ModuleInit() { go serviceRun() } // 联网保持服务 func serviceRun() { // 1, 首次打开网络 openNetwork() // 2, 监控联网状态 t := time.NewTimer(interval1) defer t.Stop() for { select { case <-t.C: // 3.1 切换网络-看情况 eth0CableOK, _ := isEth0CableConnected() if eth0CableOK && curNetType != NetEth { // 有线插入 && 当前不是有线 baseapp.Logger.Warnf("[%s] 检测到有线接入,切换到有线网络...", MODULE_NAME) openNetwork() } else if !eth0CableOK && curNetType == NetEth { // 有线拔出 && 当前还是有线 baseapp.Logger.Warnf("[%s] 检测到有线断开,切换到蜂窝网络...", MODULE_NAME) openNetwork() } // 3.2, 联网检测-看结果 dnsOK, pingOK, tcpOK, httpOK := CheckNetwork() baseapp.Logger.Infof("[%s] 联网类型: %s; 检测结果: DNS OK=%v, PING OK=%v, TCP OK=%v, HTTP OK=%v", MODULE_NAME, curNetType.String(), dnsOK, pingOK, tcpOK, httpOK) // 3.3, 联网成功-在线时 if dnsOK && pingOK && tcpOK { isOnline.Store(true) offlineStartTs.Store(0) if isSyncNTPTimeOK.Load() { if time.Since(lastSyncNTPTime) >= (time.Duration(60) * time.Minute) { // 1小时后需要再次同步NTP时间 isSyncNTPTimeOK.Store(false) } else { t.Reset(interval2) continue } } err := SyncNTPTime() if err == nil { isSyncNTPTimeOK.Store(true) baseapp.Logger.Infof("[%s] NTP时间已同步", MODULE_NAME) lastSyncNTPTime = time.Now() t.Reset(interval2) continue } else { baseapp.Logger.Errorf("[%s] 同步NTP时间时有错误发生: %v!!", MODULE_NAME, err) t.Reset(interval1) continue } } // 3.4, 联网失败-离线时 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) switch curModemType { case Air720U: //重启合宙4G调制解调器 modem1.ModuleExit() modem1.ModuleInit(true) case EC200U: // 重启移远4G调制解调器 modem2.ModuleExit() modem2.ModuleInit(true) } openNetwork() offlineStartTs.Store(time.Now().UnixNano()) // 重置离线开始时间 } t.Reset(interval1) 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 := disable4GInterfaces() 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() switch curModemType { case Air720U: //合宙4G调制解调器 modem1.Stop4GNetwork() case EC200U: // 移远4G调制解调器 modem2.Stop4GNetwork() } eth0CableOK, _ := isEth0CableConnected() if eth0CableOK && openEth0Net() { curNetType = NetEth baseapp.Logger.Infof("[%s] ✅ 有线网络已连接", MODULE_NAME) return } start4GNetwork := func() bool { startOK := false switch curModemType { case Air720U: //合宙4G调制解调器 if eth2CableOK, _ := modem1.Is4GCableConnected(); eth2CableOK { startOK = modem1.Start4GNetwork() time.Sleep(time.Duration(500) * time.Millisecond) enableEth0() // 恢复有线网口 } case EC200U: // 移远4G调制解调器 if usb0CableOK, _ := modem2.Is4GCableConnected(); usb0CableOK { startOK = modem2.Start4GNetwork() time.Sleep(time.Duration(500) * time.Millisecond) enableEth0() // 恢复有线网口 } } return startOK } if start4GNetwork() { curNetType = NetLTE baseapp.Logger.Infof("[%s] ✅ 蜂窝网络已连接(%s)", MODULE_NAME, curModemType.String()) return } curNetType = NetNone baseapp.Logger.Warnf("[%s] 当前无可用网络接口(有线/蜂窝均不可用)!", MODULE_NAME) } // 得到当前联网类型: 有线、蜂窝 func GetCurrentNetType() NetType { return curNetType } //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 }