page1_handler.go 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "net/http"
  6. "os"
  7. "os/exec"
  8. "path/filepath"
  9. "runtime"
  10. "strconv"
  11. "strings"
  12. "sync"
  13. )
  14. var lastCPU struct {
  15. idle, total uint64
  16. mu sync.Mutex
  17. }
  18. func initCPUUsage() {
  19. getCPUUsage()
  20. }
  21. func systemInfoHandler(w http.ResponseWriter, r *http.Request) {
  22. w.Header().Set("Content-Type", "application/json; charset=utf-8")
  23. memPercent, memUsed, memTotal := getMemInfo()
  24. diskPercent, diskUsed, diskTotal := getDiskInfo()
  25. json.NewEncoder(w).Encode(map[string]any{
  26. "cpu_usage": getCPUUsage(),
  27. "cpu_cores": getCPUCore(),
  28. "cpu_freq": getCPUFreq(),
  29. "cpu_temp": getCPUTemperature(),
  30. "mem_percent": memPercent,
  31. "mem_used": memUsed,
  32. "mem_total": memTotal,
  33. "disk_percent": diskPercent,
  34. "disk_used": diskUsed,
  35. "disk_total": diskTotal,
  36. })
  37. }
  38. func getCPUUsage() int {
  39. lastCPU.mu.Lock()
  40. defer lastCPU.mu.Unlock()
  41. data, err := os.ReadFile("/proc/stat")
  42. if err != nil {
  43. return 0
  44. }
  45. f := strings.Fields(strings.Split(string(data), "\n")[0])
  46. if len(f) < 5 {
  47. return 0
  48. }
  49. var idle, total uint64
  50. for i := 1; i < len(f); i++ {
  51. v, err := strconv.ParseUint(f[i], 10, 64)
  52. if err != nil {
  53. continue
  54. }
  55. total += v
  56. // idle + iowait
  57. if i == 4 || i == 5 {
  58. idle += v
  59. }
  60. }
  61. if lastCPU.total == 0 {
  62. lastCPU.idle = idle
  63. lastCPU.total = total
  64. return 0
  65. }
  66. dt := total - lastCPU.total
  67. di := idle - lastCPU.idle
  68. lastCPU.idle = idle
  69. lastCPU.total = total
  70. if dt == 0 {
  71. return 0
  72. }
  73. return int((dt - di) * 100 / dt)
  74. }
  75. func getCPUCore() int {
  76. return runtime.NumCPU()
  77. }
  78. func getCPUFreq() string {
  79. var total, count int
  80. for i := 0; ; i++ {
  81. data, err := os.ReadFile(fmt.Sprintf("/sys/devices/system/cpu/cpu%d/cpufreq/scaling_cur_freq", i))
  82. if err != nil {
  83. break
  84. }
  85. freq, err := strconv.Atoi(strings.TrimSpace(string(data)))
  86. if err != nil {
  87. continue
  88. }
  89. total += freq / 1000
  90. count++
  91. }
  92. if count > 0 {
  93. return strconv.Itoa(total / count)
  94. }
  95. data, err := os.ReadFile("/proc/cpuinfo")
  96. if err == nil {
  97. for _, line := range strings.Split(string(data), "\n") {
  98. if strings.HasPrefix(line, "cpu MHz") {
  99. f := strings.Split(line, ":")
  100. if len(f) == 2 {
  101. v, err := strconv.ParseFloat(strings.TrimSpace(f[1]), 64)
  102. if err == nil {
  103. return strconv.Itoa(int(v))
  104. }
  105. }
  106. }
  107. }
  108. }
  109. return noValue
  110. }
  111. func getCPUTemperature() string {
  112. files, _ := filepath.Glob("/sys/class/thermal/thermal_zone*/temp")
  113. max := 0
  114. for _, f := range files {
  115. data, err := os.ReadFile(f)
  116. if err != nil {
  117. continue
  118. }
  119. temp, err := strconv.Atoi(strings.TrimSpace(string(data)))
  120. if err == nil && temp > max {
  121. max = temp
  122. }
  123. }
  124. if max == 0 {
  125. return noValue
  126. }
  127. return fmt.Sprintf("%d℃", max/1000)
  128. }
  129. func getMemInfo() (int, string, string) {
  130. data, err := os.ReadFile("/proc/meminfo")
  131. if err != nil {
  132. return 0, noValue, noValue
  133. }
  134. var total, avail uint64
  135. for _, line := range strings.Split(string(data), "\n") {
  136. f := strings.Fields(line)
  137. if len(f) < 2 {
  138. continue
  139. }
  140. switch f[0] {
  141. case "MemTotal:":
  142. total, _ = strconv.ParseUint(f[1], 10, 64)
  143. case "MemAvailable:":
  144. avail, _ = strconv.ParseUint(f[1], 10, 64)
  145. }
  146. }
  147. if total == 0 {
  148. return 0, noValue, noValue
  149. }
  150. used := total - avail
  151. return int(used * 100 / total),
  152. formatSize(used * 1024),
  153. formatSize(total * 1024)
  154. }
  155. func getDiskInfo() (int, string, string) {
  156. out, err := exec.Command("df", "-h", "/").Output()
  157. if err != nil {
  158. return 0, noValue, noValue
  159. }
  160. lines := strings.Split(string(out), "\n")
  161. if len(lines) < 2 {
  162. return 0, noValue, noValue
  163. }
  164. f := strings.Fields(lines[1])
  165. if len(f) < 5 {
  166. return 0, noValue, noValue
  167. }
  168. percent, err := strconv.Atoi(strings.TrimSuffix(f[4], "%"))
  169. if err != nil {
  170. percent = 0
  171. }
  172. return percent, f[2], f[1]
  173. }
  174. func formatSize(size uint64) string {
  175. const (
  176. KB = 1024
  177. MB = KB * 1024
  178. GB = MB * 1024
  179. )
  180. switch {
  181. case size >= GB:
  182. return fmt.Sprintf("%.1fGB", float64(size)/GB)
  183. case size >= MB:
  184. return fmt.Sprintf("%.0fMB", float64(size)/MB)
  185. case size >= KB:
  186. return fmt.Sprintf("%.0fKB", float64(size)/KB)
  187. default:
  188. return fmt.Sprintf("%dB", size)
  189. }
  190. }
  191. func systemLoadAverageHandler(w http.ResponseWriter, r *http.Request) {
  192. data, err := os.ReadFile("/proc/loadavg")
  193. if err != nil {
  194. http.Error(w, err.Error(), http.StatusInternalServerError)
  195. return
  196. }
  197. fields := strings.Fields(string(data))
  198. if len(fields) < 3 {
  199. http.Error(w, "invalid /proc/loadavg", http.StatusInternalServerError)
  200. return
  201. }
  202. resp := struct {
  203. Load1m string `json:"load1m"`
  204. Load5m string `json:"load5m"`
  205. Load15m string `json:"load15m"`
  206. }{
  207. Load1m: formatLoad(fields[0]),
  208. Load5m: formatLoad(fields[1]),
  209. Load15m: formatLoad(fields[2]),
  210. }
  211. w.Header().Set("Content-Type", "application/json")
  212. json.NewEncoder(w).Encode(resp)
  213. }
  214. func formatLoad(v string) string {
  215. f, err := strconv.ParseFloat(v, 64)
  216. if err != nil {
  217. return noValue
  218. }
  219. return strconv.FormatFloat(f, 'f', 2, 64)
  220. }
  221. func serviceStatusHandler(w http.ResponseWriter, r *http.Request) {
  222. if r.Method != http.MethodGet {
  223. http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
  224. return
  225. }
  226. services := []struct {
  227. name string
  228. dir string
  229. }{
  230. {"yfkj-networkd.service", "/opt/yfkj/networkd.service"},
  231. {"yfkj-timesyncd.service", "/opt/yfkj/timesyncd.service"},
  232. {"yfkj-gnss.service", "/opt/yfkj/gnss.service"},
  233. {"yfkj-sshd-mqtt-bridge.service", "/opt/yfkj/sshd-mqtt-bridge.service"},
  234. {"yfkj-camera-capture.service", "/opt/yfkj/camera-capture.service"},
  235. {"yfkj-app-install.service", "/opt/yfkj/app-install.service"},
  236. {"yfkj-web-ui.service", "/opt/yfkj/web-ui.service"},
  237. {"yfkj-upgrade.service", "/opt/yfkj/upgrade.service"},
  238. }
  239. data := make(map[string]string, len(services)*2)
  240. for i, s := range services {
  241. n := i + 1
  242. status := noValue
  243. if out, err := exec.Command("systemctl", "is-active", s.name).Output(); err == nil {
  244. if strings.TrimSpace(string(out)) == "active" {
  245. status = "🟢运行中..."
  246. }
  247. }
  248. data[fmt.Sprintf("var%d", n)] = status
  249. if v, err := os.ReadFile(filepath.Join(s.dir, "version.txt")); err == nil {
  250. data[fmt.Sprintf("var%d%d", n, n)] = "v" + strings.TrimSpace(string(v))
  251. } else {
  252. data[fmt.Sprintf("var%d%d", n, n)] = noValue
  253. }
  254. }
  255. w.Header().Set("Content-Type", "application/json")
  256. json.NewEncoder(w).Encode(data)
  257. }