diff --git a/app/app.go b/app/app.go index af44ce1..9a24875 100644 --- a/app/app.go +++ b/app/app.go @@ -2,7 +2,7 @@ package app import ( "finally-a-monolithic-linux-hw-monitor/app/mem" - proc "finally-a-monolithic-linux-hw-monitor/app/proc" + "finally-a-monolithic-linux-hw-monitor/app/proc" "fmt" "os" "os/signal" @@ -25,8 +25,8 @@ func Run() { os.Exit(0) }() - p, _ := proc.GetProc() - m, _ := mem.GetMem() + proc, _ := proc.GetProc() + mem, _ := mem.GetMem() ticker := time.NewTicker(1 * time.Second) defer ticker.Stop() @@ -35,8 +35,8 @@ func Run() { // Move cursor to top-left (0,0) without clearing full buffer (flicker-free) fmt.Fprint(console, "\033[H") - proc.RenderProc(console, &p) - mem.RenderMem(console, &m) + proc.RenderProc(console) + mem.Render(console) fmt.Fprint(console, "\nPress Ctrl+C to exit.\033[K\n") fmt.Print(console.String()) diff --git a/app/common/drivers.go b/app/common/drivers.go new file mode 100644 index 0000000..477d509 --- /dev/null +++ b/app/common/drivers.go @@ -0,0 +1,8 @@ +package common + +import "strings" + +type Driver interface { + Read() (any, error) + Render(*strings.Builder) error +} diff --git a/app/mem/drivers/dmidecode.go b/app/mem/drivers/dmidecode.go index 16664d1..a275e8b 100644 --- a/app/mem/drivers/dmidecode.go +++ b/app/mem/drivers/dmidecode.go @@ -10,12 +10,36 @@ import ( type DmiDecodeDriver struct{} -func NewDmiDecodeDriver() *DmiDecodeDriver { - return &DmiDecodeDriver{} +func NewDmiDecodeDriver() (*DmiDecodeDriver, error) { + r := DmiDecodeDriver{} + _, err := r.Read() + return &r, err +} + +func (r *DmiDecodeDriver) Render(console *strings.Builder) error { + h, err := r.Read() + if err != nil { + return err + } + + hardware := h.(MemoryHardware) + + for i := range len(hardware.ModelNames) { + fmt.Fprintf(console, "--- RAM Module %d ---\n", i) + modelName := hardware.ModelNames[i] + fmt.Fprintf(console, "%-16s | %-16s \033[K\n", "Model Name", modelName) + generation := hardware.Generations[i] + speed := hardware.Speeds[i] + fmt.Fprintf(console, "%-16s | %s@%s \033[K\n", "Speed", generation, speed) + formFactor := hardware.FormFactors[i] + fmt.Fprintf(console, "%-16s | %s \033[K\n", "Form Factor", formFactor) + } + + return nil } // Parses dmidecode to extract physical hardware limits, slot counts, and module details -func (r *DmiDecodeDriver) ReadHardwareSensors() (MemoryHardware, error) { +func (r *DmiDecodeDriver) Read() (any, error) { hw := MemoryHardware{} // Query Memory Array (Type 16) for Max Capacity and Slot Count diff --git a/app/mem/drivers/drivers.go b/app/mem/drivers/drivers.go index 1cc48f0..696f33b 100644 --- a/app/mem/drivers/drivers.go +++ b/app/mem/drivers/drivers.go @@ -8,10 +8,6 @@ type MemoryUsage struct { UsedPercent float64 } -type UsageDriver interface { - ReadUsageSensors() (MemoryUsage, error) -} - // MemoryHardware holds physical RAM slot and module details type MemoryHardware struct { MaxCapacityGB float64 @@ -22,7 +18,3 @@ type MemoryHardware struct { FormFactors []string ModelNames []string } - -type HardwareDriver interface { - ReadHardwareSensors() (MemoryHardware, error) -} diff --git a/app/mem/drivers/meminfo.go b/app/mem/drivers/meminfo.go index 3bb9f8c..f1cf4de 100644 --- a/app/mem/drivers/meminfo.go +++ b/app/mem/drivers/meminfo.go @@ -2,6 +2,7 @@ package drivers import ( "bufio" + "fmt" "os" "strconv" "strings" @@ -9,12 +10,30 @@ import ( type MemInfoDriver struct{} -func NewMemInfoDriver() *MemInfoDriver { - return &MemInfoDriver{} +func NewMemInfoDriver() (*MemInfoDriver, error) { + r := MemInfoDriver{} + _, err := r.Read() + return &r, err +} + +func (r *MemInfoDriver) Render(console *strings.Builder) error { + u, err := r.Read() + if err != nil { + return err + } + + usage := u.(MemoryUsage) + + fmt.Fprintf(console, "--- Usage ---\n") + fmt.Fprintf(console, "%-16s | %.2f GiB\033[K\n", "Total", usage.TotalGB) + fmt.Fprintf(console, "%-16s | %.2f GiB (%.2f%%) \033[K\n", "Used", usage.UsedGB, usage.UsedPercent) + fmt.Fprintf(console, "%-16s | %.2f GiB\033[K\n", "Avail", usage.AvailableGB) + + return nil } // Read /proc/meminfo for active system memory statistics -func (r *MemInfoDriver) ReadUsageSensors() (MemoryUsage, error) { +func (r *MemInfoDriver) Read() (any, error) { usage := MemoryUsage{} file, err := os.Open("/proc/meminfo") if err != nil { diff --git a/app/mem/mem.go b/app/mem/mem.go index 1604458..8123a55 100644 --- a/app/mem/mem.go +++ b/app/mem/mem.go @@ -1,69 +1,36 @@ package mem import ( + "finally-a-monolithic-linux-hw-monitor/app/common" "finally-a-monolithic-linux-hw-monitor/app/mem/drivers" "fmt" "strings" ) -func RenderMem(console *strings.Builder, mem *Mem) { +func (mem *Mem) Render(console *strings.Builder) { fmt.Fprintf(console, "=== %s ===\n", "Memory") - RenderUsageSensors(console, mem) - RenderHardwareSensors(console, mem) -} - -func RenderUsageSensors(console *strings.Builder, mem *Mem) { - usage, _ := mem.SensorReader.ReadUsageSensors() - fmt.Fprintf(console, "--- Usage ---\n") - fmt.Fprintf(console, "%-16s | %.2f GiB\033[K\n", "Total", usage.TotalGB) - fmt.Fprintf(console, "%-16s | %.2f GiB (%.2f%%) \033[K\n", "Used", usage.UsedGB, usage.UsedPercent) - fmt.Fprintf(console, "%-16s | %.2f GiB\033[K\n", "Avail", usage.AvailableGB) -} - -func RenderHardwareSensors(console *strings.Builder, mem *Mem) { - hardware, _ := mem.SensorReader.ReadHardwareSensors() - for i := range len(hardware.ModelNames) { - fmt.Fprintf(console, "--- RAM Module %d ---\n", i) - modelName := hardware.ModelNames[i] - fmt.Fprintf(console, "%-16s | %-16s \033[K\n", "Model Name", modelName) - generation := hardware.Generations[i] - speed := hardware.Speeds[i] - fmt.Fprintf(console, "%-16s | %s@%s \033[K\n", "Speed", generation, speed) - formFactor := hardware.FormFactors[i] - fmt.Fprintf(console, "%-16s | %s \033[K\n", "Form Factor", formFactor) + for _, driver := range mem.drivers { + driver.Render(console) } } type Mem struct { - SensorReader MemSensorReader + drivers []common.Driver } func GetMem() (Mem, error) { - memInfoDriver := drivers.NewMemInfoDriver() - dmiDecodeDriver := drivers.NewDmiDecodeDriver() - reader := &StandardMemSensorReader{ - UsageReader: memInfoDriver, - HardwareReader: dmiDecodeDriver, + mem := Mem{} + mem.drivers = []common.Driver{} + + meminfo, err := drivers.NewMemInfoDriver() + if err == nil { + mem.drivers = append(mem.drivers, meminfo) } - return Mem{ - SensorReader: reader, - }, nil -} -type MemSensorReader interface { - ReadUsageSensors() (drivers.MemoryUsage, error) - ReadHardwareSensors() (drivers.MemoryHardware, error) -} + dmidecode, err := drivers.NewDmiDecodeDriver() + if err == nil { + mem.drivers = append(mem.drivers, dmidecode) + } -type StandardMemSensorReader struct { - UsageReader drivers.UsageDriver - HardwareReader drivers.HardwareDriver -} - -func (r *StandardMemSensorReader) ReadUsageSensors() (drivers.MemoryUsage, error) { - return r.UsageReader.ReadUsageSensors() -} - -func (r *StandardMemSensorReader) ReadHardwareSensors() (drivers.MemoryHardware, error) { - return r.HardwareReader.ReadHardwareSensors() + return mem, nil } diff --git a/app/proc/drivers/drivers.go b/app/proc/drivers/drivers.go index c616b16..67a4965 100644 --- a/app/proc/drivers/drivers.go +++ b/app/proc/drivers/drivers.go @@ -6,27 +6,14 @@ type TemperatureSensor struct { TempC float64 } -type TemperatureDriver interface { - ReadTemperatureSensors() ([]TemperatureSensor, error) -} - type PowerSensor struct { path string Name string Watts float64 } - -type PowerDriver interface { - ReadPowerSensors() ([]PowerSensor, error) -} - type ClockSensor struct { path string number int Name string FrequencyGHz float64 } - -type ClockDriver interface { - ReadClockSensors() ([]ClockSensor, error) -} diff --git a/app/proc/drivers/hwmon.go b/app/proc/drivers/hwmon.go index 56a3946..f9d60eb 100644 --- a/app/proc/drivers/hwmon.go +++ b/app/proc/drivers/hwmon.go @@ -13,11 +13,11 @@ type HwmonDriver struct { hwmonDriver string } -func NewHwmonDriver(expectedDrivers []string) *HwmonDriver { +func NewHwmonDriver(expectedDrivers []string) (*HwmonDriver, error) { // Locate hwmon path dirs, err := filepath.Glob("/sys/class/hwmon/hwmon*") if err != nil { - return nil + return nil, err } for _, dir := range dirs { @@ -29,16 +29,35 @@ func NewHwmonDriver(expectedDrivers []string) *HwmonDriver { return &HwmonDriver{ hwmonDir: dir, hwmonDriver: driver, - } + }, nil } } } } + return nil, fmt.Errorf("could not find %#v in /sys/class/hwmon", expectedDrivers) +} + +func (r *HwmonDriver) Render(console *strings.Builder) error { + t, err := r.Read() + if err != nil { + return err + } + + temps := t.([]TemperatureSensor) + + fmt.Fprint(console, "--- Temperature ---\n") + for _, sensor := range temps { + tempC := sensor.TempC + tempF := (tempC * 9 / 5) + 32 + // \033[K clears from cursor to end of line (prevents lingering chars) + fmt.Fprintf(console, "%-16s | %.2f °C (%.3f °F)\033[K\n", sensor.Name, tempC, tempF) + } + return nil } -func (r *HwmonDriver) ReadTemperatureSensors() ([]TemperatureSensor, error) { +func (r *HwmonDriver) Read() (any, error) { inputs, err := filepath.Glob(filepath.Join(r.hwmonDir, "temp*_input")) if err != nil || len(inputs) == 0 { return nil, fmt.Errorf("no temperature inputs found in %s", r.hwmonDir) diff --git a/app/proc/drivers/rapl.go b/app/proc/drivers/rapl.go index 3a43fd1..17790a7 100644 --- a/app/proc/drivers/rapl.go +++ b/app/proc/drivers/rapl.go @@ -20,14 +20,32 @@ type RaplDriver struct { energyState map[string]EnergyState } -func NewRaplDriver() *RaplDriver { - return &RaplDriver{ +func NewRaplDriver() (*RaplDriver, error) { + r := RaplDriver{ energyState: map[string]EnergyState{}, } - + _, err := r.Read() + return &r, err } -func (r *RaplDriver) ReadPowerSensors() ([]PowerSensor, error) { +func (r *RaplDriver) Render(console *strings.Builder) error { + fmt.Fprint(console, "--- Power ---\n") + p, err := r.Read() + if err != nil { + return err + } + + power := p.([]PowerSensor) + + for _, sensor := range power { + // \033[K clears from cursor to end of line (prevents lingering chars) + fmt.Fprintf(console, "%-16s | %.2f W\033[K\n", sensor.Name, sensor.Watts) + } + + return nil +} + +func (r *RaplDriver) Read() (any, error) { powerSensors := []PowerSensor{} patterns := []string{ diff --git a/app/proc/drivers/sys.go b/app/proc/drivers/sys.go index 017cff2..2d4d89b 100644 --- a/app/proc/drivers/sys.go +++ b/app/proc/drivers/sys.go @@ -11,11 +11,30 @@ import ( type SysDriver struct{} -func NewSysDriver() *SysDriver { - return &SysDriver{} +func NewSysDriver() (*SysDriver, error) { + r := SysDriver{} + _, err := r.Read() + return &r, err } -func (r *SysDriver) ReadClockSensors() ([]ClockSensor, error) { +func (r *SysDriver) Render(console *strings.Builder) error { + fmt.Fprint(console, "--- Clock Frequency ---\n") + c, err := r.Read() + if err != nil { + return err + } + + clock := c.([]ClockSensor) + + for _, sensor := range clock { + // \033[K clears from cursor to end of line (prevents lingering chars) + fmt.Fprintf(console, "%-16s | %.2f GHz\033[K\n", sensor.Name, sensor.FrequencyGHz) + } + + return nil +} + +func (r *SysDriver) Read() (any, error) { // Glob directly targets scaling_cur_freq for all numerical CPU core paths freqFiles, err := filepath.Glob("/sys/devices/system/cpu/cpu[0-9]*/cpufreq/scaling_cur_freq") if err != nil { diff --git a/app/proc/proc.go b/app/proc/proc.go index b5376af..e67c1f4 100644 --- a/app/proc/proc.go +++ b/app/proc/proc.go @@ -2,58 +2,22 @@ package proc import ( "bufio" - "finally-a-monolithic-linux-hw-monitor/app/proc/drivers" + "finally-a-monolithic-linux-hw-monitor/app/common" "fmt" "os" "strings" ) -func RenderProc(console *strings.Builder, proc *Proc) { +func (proc *Proc) RenderProc(console *strings.Builder) { fmt.Fprintf(console, "=== %s ===\n", proc.Model()) - - RenderTemperatureSensors(console, proc) - RenderPowerSensors(console, proc) - RenderClockSensors(console, proc) -} - -func RenderTemperatureSensors(console *strings.Builder, proc *Proc) { - temps, _ := proc.SensorReader.ReadTemperatureSensors() - fmt.Fprint(console, "--- Temperature ---\n") - for _, sensor := range temps { - tempC := sensor.TempC - tempF := (tempC * 9 / 5) + 32 - // \033[K clears from cursor to end of line (prevents lingering chars) - fmt.Fprintf(console, "%-16s | %.2f °C (%.3f °F)\033[K\n", sensor.Name, tempC, tempF) - } -} - -func RenderPowerSensors(console *strings.Builder, proc *Proc) { - fmt.Fprint(console, "--- Power ---\n") - power, _ := proc.SensorReader.ReadPowerSensors() - for _, sensor := range power { - // \033[K clears from cursor to end of line (prevents lingering chars) - fmt.Fprintf(console, "%-16s | %.2f W\033[K\n", sensor.Name, sensor.Watts) - } -} - -func RenderClockSensors(console *strings.Builder, proc *Proc) { - fmt.Fprint(console, "--- Clock Frequency ---\n") - power, _ := proc.SensorReader.ReadClockSensors() - for _, sensor := range power { - // \033[K clears from cursor to end of line (prevents lingering chars) - fmt.Fprintf(console, "%-16s | %.2f GHz\033[K\n", sensor.Name, sensor.FrequencyGHz) + for _, driver := range proc.drivers { + driver.Render(console) } } type Proc struct { - SensorReader ProcSensorReader - Info map[string]string -} - -type ProcSensorReader interface { - ReadTemperatureSensors() ([]drivers.TemperatureSensor, error) - ReadPowerSensors() ([]drivers.PowerSensor, error) - ReadClockSensors() ([]drivers.ClockSensor, error) + drivers []common.Driver + Info map[string]string } const ( @@ -96,12 +60,12 @@ func GetProc() (Proc, error) { switch vendor { case VendorIntel: - intelReader, err := NewIntelReader() - proc.SensorReader = intelReader + drivers, err := NewIntelDrivers() + proc.drivers = drivers return proc, err case VendorAMD: - amdReader, err := NewAMDReader() - proc.SensorReader = amdReader + drivers, err := NewAMDDrivers() + proc.drivers = drivers return proc, err default: return proc, fmt.Errorf("proc type %s is not supported", vendor) diff --git a/app/proc/vendor_amd.go b/app/proc/vendor_amd.go index fe3350d..271e64f 100644 --- a/app/proc/vendor_amd.go +++ b/app/proc/vendor_amd.go @@ -1,47 +1,31 @@ package proc import ( + "finally-a-monolithic-linux-hw-monitor/app/common" driver "finally-a-monolithic-linux-hw-monitor/app/proc/drivers" "fmt" ) -type AMDReader struct { - temperatureDriver driver.TemperatureDriver - powerDriver driver.PowerDriver - clockDriver driver.ClockDriver -} +func NewAMDDrivers() ([]common.Driver, error) { + drivers := []common.Driver{} -func NewAMDReader() (*AMDReader, error) { - temperatureDriver := driver.NewHwmonDriver([]string{"k10temp", "zenpower"}) - if temperatureDriver == nil { + temperatureDriver, err := driver.NewHwmonDriver([]string{"k10temp", "zenpower"}) + if err != nil { return nil, fmt.Errorf("could not initialize hwmon temperature driver") } + drivers = append(drivers, temperatureDriver) - powerDriver := driver.NewRaplDriver() - if powerDriver == nil { + powerDriver, err := driver.NewRaplDriver() + if err != nil { return nil, fmt.Errorf("could not initialize hwmon temperature driver") } + drivers = append(drivers, powerDriver) - clockDriver := driver.NewSysDriver() - if clockDriver == nil { + clockDriver, err := driver.NewSysDriver() + if err != nil { return nil, fmt.Errorf("could not initialize hwmon temperature driver") } + drivers = append(drivers, clockDriver) - return &AMDReader{ - temperatureDriver: temperatureDriver, - powerDriver: powerDriver, - clockDriver: clockDriver, - }, nil -} - -func (r *AMDReader) ReadTemperatureSensors() ([]driver.TemperatureSensor, error) { - return r.temperatureDriver.ReadTemperatureSensors() -} - -func (r *AMDReader) ReadPowerSensors() ([]driver.PowerSensor, error) { - return r.powerDriver.ReadPowerSensors() -} - -func (r *AMDReader) ReadClockSensors() ([]driver.ClockSensor, error) { - return r.clockDriver.ReadClockSensors() + return drivers, nil } diff --git a/app/proc/vendor_intel.go b/app/proc/vendor_intel.go index d8f4c63..cc91541 100644 --- a/app/proc/vendor_intel.go +++ b/app/proc/vendor_intel.go @@ -1,47 +1,30 @@ package proc import ( + "finally-a-monolithic-linux-hw-monitor/app/common" driver "finally-a-monolithic-linux-hw-monitor/app/proc/drivers" "fmt" ) -type IntelReader struct { - temperatureDriver driver.TemperatureDriver - powerDriver driver.PowerDriver - clockDriver driver.ClockDriver -} - -func NewIntelReader() (*IntelReader, error) { - temperatureDriver := driver.NewHwmonDriver([]string{"coretemp"}) - if temperatureDriver == nil { +func NewIntelDrivers() ([]common.Driver, error) { + drivers := []common.Driver{} + temperatureDriver, err := driver.NewHwmonDriver([]string{"coretemp"}) + if err != nil { return nil, fmt.Errorf("could not initialize hwmon temperature driver") } + drivers = append(drivers, temperatureDriver) - powerDriver := driver.NewRaplDriver() - if powerDriver == nil { + powerDriver, err := driver.NewRaplDriver() + if err != nil { return nil, fmt.Errorf("could not initialize hwmon temperature driver") } + drivers = append(drivers, powerDriver) - clockDriver := driver.NewSysDriver() - if clockDriver == nil { + clockDriver, err := driver.NewSysDriver() + if err != nil { return nil, fmt.Errorf("could not initialize hwmon temperature driver") } + drivers = append(drivers, clockDriver) - return &IntelReader{ - temperatureDriver: temperatureDriver, - powerDriver: powerDriver, - clockDriver: clockDriver, - }, nil -} - -func (r *IntelReader) ReadTemperatureSensors() ([]driver.TemperatureSensor, error) { - return r.temperatureDriver.ReadTemperatureSensors() -} - -func (r *IntelReader) ReadPowerSensors() ([]driver.PowerSensor, error) { - return r.powerDriver.ReadPowerSensors() -} - -func (r *IntelReader) ReadClockSensors() ([]driver.ClockSensor, error) { - return r.clockDriver.ReadClockSensors() + return drivers, nil }