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 // 标记是否联网 offlineStartTime time.Time // 离线开始时间 enableTimeSync atomic.Bool /// 是否启用时间同步(默认启用) isSyncNTPTimeOK atomic.Bool /// 标记本地时间是否已同步成功 lastSyncNTPTime time.Time /// 记录同步网络时间成功的时间 curNetType atomic.Int32 /// 当前的网络类型: 有线、蜂窝 mu1 sync.Mutex isRunning1 bool exitCh1 chan struct{} wg1 sync.WaitGroup ) type NetType int32 const ( NetNone NetType = iota NetEth // 有线网络, 注册网卡名为"eth0" NetLTE // 蜂窝网络 ) func (n NetType) String() string { switch n { case NetEth: return "有线" case NetLTE: return "蜂窝" default: return "未知" } } func setNetType(n NetType) { curNetType.Store(int32(n)) } func getNetType() NetType { return NetType(curNetType.Load()) } func init() { enableTimeSync.Store(true) // 默认开启时间同步 } func SetTimeSyncEnabled(enable bool) { enableTimeSync.Store(enable) } const ( interval1 = time.Duration(1) * time.Second interval2 = time.Duration(5) * time.Second ) func ModuleInit() { offlineStartTime = time.Now() 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 && getNetType() != NetEth { // 有线插入 && 当前不是有线 baseapp.Logger.Warnf("[%s] 检测到有线接入,正在尝试切换到有线网络...", MODULE_NAME) openNetwork() } else if !eth0CableOK && getNetType() == NetEth { // 有线拔出 && 当前还是有线 baseapp.Logger.Warnf("[%s] 检测到有线断开,正在尝试切换到蜂窝网络...", MODULE_NAME) openNetwork() } // 3.2, 联网检测-看结果 dnsOK, pingOK, tcpOK, httpOK, httpTime := CheckNetwork() baseapp.Logger.Infof("[%s] 联网类型: %s; 检测结果: DNS OK=%v, PING OK=%v, TCP OK=%v, HTTP OK=%v", MODULE_NAME, getNetType().String(), dnsOK, pingOK, tcpOK, httpOK) // 3.3, 联网成功-在线时 netOK := tcpOK || pingOK if netOK { isOnline.Store(true) if !enableTimeSync.Load() { // 时间同步功能被禁用, 下面不再同步 isSyncNTPTimeOK.Store(false) t.Reset(interval2) continue } if isSyncNTPTimeOK.Load() && time.Since(lastSyncNTPTime) < (time.Duration(60)*time.Minute) { t.Reset(interval2) continue } var err error if !httpTime.IsZero() { // 优先使用HTTP时间, 实测速度快且精度高 err = SetSystemTime(httpTime) } if httpTime.IsZero() || err != nil { err = SyncNTPTime() //// 同步HTTP时间失败, 则尝试同步标准时间 } if err == nil { isSyncNTPTimeOK.Store(true) baseapp.Logger.Infof("[%s] ✅ 系统时间已同步: %s", MODULE_NAME, time.Now().Format("2006-01-02 15:04:05")) lastSyncNTPTime = time.Now() t.Reset(interval2) continue } else { isSyncNTPTimeOK.Store(false) baseapp.Logger.Errorf("[%s] 同步系统时间时有错误发生: %v!!", MODULE_NAME, err) t.Reset(interval1) continue } } // 3.4, 联网失败-离线时 if isOnline.Load() { // 状态由"1"变为"0" offlineStartTime = time.Now() // 记录离线开始时间 isOnline.Store(false) } if time.Since(offlineStartTime) >= (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() offlineStartTime = time.Now() // 重置离线开始时间 } 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() { if !enableTimeSync.Load() || IsSyncedNtpTime() { return true } } remaining := time.Until(deadline) if remaining <= 0 { return false } sleep := min(tick, remaining) time.Sleep(sleep) } } // 打开有线网络 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() { setNetType(NetEth) baseapp.Logger.Infof("[%s] ✅ 有线网络已连接", MODULE_NAME) return } if getNetType() == NetLTE && IsInetAvailable() { // 当前已是蜂窝网络且联网正常 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() { setNetType(NetLTE) baseapp.Logger.Infof("[%s] ✅ 蜂窝网络已连接(%s)", MODULE_NAME, curModemType.String()) return } setNetType(NetNone) baseapp.Logger.Warnf("[%s] 当前无可用网络接口(有线/蜂窝均不可用)!", MODULE_NAME) } // 得到当前联网类型: 有线、蜂窝 func GetCurrentNetType() NetType { return getNetType() } //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 }