@@ -189,13 +189,6 @@ class PadNtpSource:
189189 poll = 14
190190
191191
192- class PadService :
193- name = 16
194- status = 8
195- pid = 8
196- description = 40
197-
198-
199192class PadWifiScan :
200193 ssid = 40
201194 encryption = 30
@@ -217,6 +210,34 @@ class PadDiskUsage:
217210 avail = 12
218211 percent = 6
219212
213+
214+ def format_memory_bytes (bytes_val ):
215+ """Convert bytes to human-readable format"""
216+ if bytes_val == 0 :
217+ return " "
218+ elif bytes_val < 1024 :
219+ return f"{ bytes_val } B"
220+ elif bytes_val < 1024 * 1024 :
221+ return f"{ bytes_val // 1024 } K"
222+ elif bytes_val < 1024 * 1024 * 1024 :
223+ return f"{ bytes_val // (1024 * 1024 ):.1f} M"
224+ else :
225+ return f"{ bytes_val // (1024 * 1024 * 1024 ):.1f} G"
226+
227+
228+ def format_uptime_seconds (seconds ):
229+ """Convert seconds to compact time format"""
230+ if seconds == 0 :
231+ return " "
232+ elif seconds < 60 :
233+ return f"{ seconds } s"
234+ elif seconds < 3600 :
235+ return f"{ seconds // 60 } m"
236+ elif seconds < 86400 :
237+ return f"{ seconds // 3600 } h"
238+ else :
239+ return f"{ seconds // 86400 } d"
240+
220241 @classmethod
221242 def table_width (cls ):
222243 """Total width of disk usage table"""
@@ -259,6 +280,69 @@ def table_width(cls):
259280 return cls .zone_locked + cls .zone_name + cls .zone_type + cls .zone_data \
260281 + cls .zone_services
261282
283+ class Column :
284+ """Column definition for SimpleTable"""
285+ def __init__ (self , name , length , align = 'left' , formatter = None ):
286+ self .name = name # Header text
287+ self .width = length # Max visible data length (excluding ANSI codes)
288+ self .align = align # 'left' or 'right' (defaults to 'left')
289+ self .formatter = formatter # Optional function to format values
290+
291+ class SimpleTable :
292+ """Simple table formatter that handles ANSI colors correctly"""
293+
294+ def __init__ (self , columns ):
295+ self .columns = columns
296+
297+ @staticmethod
298+ def visible_width (text ):
299+ """Return visible character count, excluding ANSI escape sequences"""
300+ ansi_pattern = r'\x1b\[[0-9;]*m'
301+ clean_text = re .sub (ansi_pattern , '' , str (text ))
302+ return len (clean_text )
303+
304+ def _format_column (self , value , column ):
305+ """Format a single column value with proper alignment
306+
307+ The column length specifies max expected data length.
308+ Framework automatically adds 1 space for column separation.
309+ """
310+ if column .formatter :
311+ value = column .formatter (value )
312+
313+ value_str = str (value )
314+ visible_len = self .visible_width (value_str )
315+
316+ if column .align == 'right' :
317+ padding = column .width - visible_len
318+ return ' ' * max (0 , padding ) + value_str + ' '
319+ else : # left alignment (default)
320+ padding = column .width - visible_len
321+ return value_str + ' ' * max (0 , padding ) + ' '
322+
323+ def header (self , styled = True ):
324+ """Generate formatted header row"""
325+ header_parts = []
326+ for column in self .columns :
327+ if column .align == 'right' :
328+ header_parts .append (f"{ column .name :>{column .width }} " )
329+ else : # left alignment (default)
330+ header_parts .append (f"{ column .name :{column .width }} " )
331+
332+ header_str = '' .join (header_parts )
333+ return Decore .invert (header_str ) if styled else header_str
334+
335+ def row (self , * values ):
336+ """Generate formatted data row"""
337+ if len (values ) != len (self .columns ):
338+ raise ValueError (f"Expected { len (self .columns )} values, got { len (values )} " )
339+
340+ row_parts = []
341+ for value , column in zip (values , self .columns ):
342+ row_parts .append (self ._format_column (value , column ))
343+
344+ return '' .join (row_parts ).rstrip ()
345+
262346
263347class Decore ():
264348 @staticmethod
@@ -1661,17 +1745,27 @@ def show_services(json):
16611745 services_data = get_json_data ({}, json , 'ietf-system:system-state' , 'infix-system:services' )
16621746 services = services_data .get ("service" , [])
16631747
1664- hdr = (f"{ 'NAME' :<{PadService .name }} "
1665- f"{ 'STATUS' :<{PadService .status }} "
1666- f"{ 'PID' :>{PadService .pid - 1 }} "
1667- f" { 'DESCRIPTION' } " )
1668- print (Decore .invert (hdr ))
1748+ # This is the first usage of simple table. I assume this will be
1749+ # copied so I left a lot of comments. If you copy it feel free
1750+ # to be less verbose..
1751+ service_table = SimpleTable ([
1752+ Column ('NAME' , 15 , 'left' ), # Max service name length
1753+ Column ('STATUS' , 10 , 'left' ), # Max status text length (e.g., "running")
1754+ Column ('PID' , 7 , 'right' ), # Max PID digits
1755+ Column ('MEM' , 6 , 'right' ), # Max memory string (e.g., "123.4M")
1756+ Column ('UP' , 4 , 'right' ), # Max uptime string (e.g., "3d")
1757+ Column ('RST' , 3 , 'right' ), # Max restart count digits
1758+ Column ('DESCRIPTION' , 30 ) # Last column needs no padding
1759+ ])
1760+
1761+ print (service_table .header ())
16691762
16701763 for svc in services :
16711764 name = svc .get ('name' , '' )
16721765 status = svc .get ('status' , '' )
16731766 pid = svc .get ('pid' , 0 )
16741767 description = svc .get ('description' , '' )
1768+ stats = svc .get ('statistics' , {})
16751769
16761770 if status in ('running' , 'active' , 'done' ):
16771771 status_str = Decore .green (status )
@@ -1680,13 +1774,17 @@ def show_services(json):
16801774 else :
16811775 status_str = Decore .yellow (status )
16821776
1683- pid_str = str (pid ) if pid > 0 else '- '
1777+ pid_str = str (pid ) if pid > 0 else ' '
16841778
1685- row = f"{ name :<{PadService .name }} "
1686- row += f"{ status_str :<{PadService .status + 9 }} "
1687- row += f"{ pid_str :>{PadService .pid }} "
1688- row += f" { description } "
1689- print (row )
1779+ memory_bytes = int (stats .get ('memory-usage' , 0 ))
1780+ uptime_secs = int (stats .get ('uptime' , 0 ))
1781+ restart_count = stats .get ('restart-count' , 0 )
1782+
1783+ memory_str = format_memory_bytes (memory_bytes )
1784+ uptime_str = format_uptime_seconds (uptime_secs )
1785+
1786+ print (service_table .row (name , status_str , pid_str , memory_str ,
1787+ uptime_str , restart_count , description ))
16901788
16911789
16921790def show_hardware (json ):
0 commit comments