@@ -190,13 +190,6 @@ class PadNtpSource:
190190 poll = 14
191191
192192
193- class PadService :
194- name = 16
195- status = 8
196- pid = 8
197- description = 40
198-
199-
200193class PadWifiScan :
201194 ssid = 40
202195 encryption = 30
@@ -218,6 +211,34 @@ class PadDiskUsage:
218211 avail = 12
219212 percent = 6
220213
214+
215+ def format_memory_bytes (bytes_val ):
216+ """Convert bytes to human-readable format"""
217+ if bytes_val == 0 :
218+ return " "
219+ elif bytes_val < 1024 :
220+ return f"{ bytes_val } B"
221+ elif bytes_val < 1024 * 1024 :
222+ return f"{ bytes_val // 1024 } K"
223+ elif bytes_val < 1024 * 1024 * 1024 :
224+ return f"{ bytes_val // (1024 * 1024 ):.1f} M"
225+ else :
226+ return f"{ bytes_val // (1024 * 1024 * 1024 ):.1f} G"
227+
228+
229+ def format_uptime_seconds (seconds ):
230+ """Convert seconds to compact time format"""
231+ if seconds == 0 :
232+ return " "
233+ elif seconds < 60 :
234+ return f"{ seconds } s"
235+ elif seconds < 3600 :
236+ return f"{ seconds // 60 } m"
237+ elif seconds < 86400 :
238+ return f"{ seconds // 3600 } h"
239+ else :
240+ return f"{ seconds // 86400 } d"
241+
221242 @classmethod
222243 def table_width (cls ):
223244 """Total width of disk usage table"""
@@ -260,6 +281,69 @@ def table_width(cls):
260281 return cls .zone_locked + cls .zone_name + cls .zone_type + cls .zone_data \
261282 + cls .zone_services
262283
284+ class Column :
285+ """Column definition for SimpleTable"""
286+ def __init__ (self , name , length , align = 'left' , formatter = None ):
287+ self .name = name # Header text
288+ self .width = length # Max visible data length (excluding ANSI codes)
289+ self .align = align # 'left' or 'right' (defaults to 'left')
290+ self .formatter = formatter # Optional function to format values
291+
292+ class SimpleTable :
293+ """Simple table formatter that handles ANSI colors correctly"""
294+
295+ def __init__ (self , columns ):
296+ self .columns = columns
297+
298+ @staticmethod
299+ def visible_width (text ):
300+ """Return visible character count, excluding ANSI escape sequences"""
301+ ansi_pattern = r'\x1b\[[0-9;]*m'
302+ clean_text = re .sub (ansi_pattern , '' , str (text ))
303+ return len (clean_text )
304+
305+ def _format_column (self , value , column ):
306+ """Format a single column value with proper alignment
307+
308+ The column length specifies max expected data length.
309+ Framework automatically adds 1 space for column separation.
310+ """
311+ if column .formatter :
312+ value = column .formatter (value )
313+
314+ value_str = str (value )
315+ visible_len = self .visible_width (value_str )
316+
317+ if column .align == 'right' :
318+ padding = column .width - visible_len
319+ return ' ' * max (0 , padding ) + value_str + ' '
320+ else : # left alignment (default)
321+ padding = column .width - visible_len
322+ return value_str + ' ' * max (0 , padding ) + ' '
323+
324+ def header (self , styled = True ):
325+ """Generate formatted header row"""
326+ header_parts = []
327+ for column in self .columns :
328+ if column .align == 'right' :
329+ header_parts .append (f"{ column .name :>{column .width }} " )
330+ else : # left alignment (default)
331+ header_parts .append (f"{ column .name :{column .width }} " )
332+
333+ header_str = '' .join (header_parts )
334+ return Decore .invert (header_str ) if styled else header_str
335+
336+ def row (self , * values ):
337+ """Generate formatted data row"""
338+ if len (values ) != len (self .columns ):
339+ raise ValueError (f"Expected { len (self .columns )} values, got { len (values )} " )
340+
341+ row_parts = []
342+ for value , column in zip (values , self .columns ):
343+ row_parts .append (self ._format_column (value , column ))
344+
345+ return '' .join (row_parts ).rstrip ()
346+
263347
264348class Decore ():
265349 @staticmethod
@@ -1693,17 +1777,27 @@ def show_services(json):
16931777 services_data = get_json_data ({}, json , 'ietf-system:system-state' , 'infix-system:services' )
16941778 services = services_data .get ("service" , [])
16951779
1696- hdr = (f"{ 'NAME' :<{PadService .name }} "
1697- f"{ 'STATUS' :<{PadService .status }} "
1698- f"{ 'PID' :>{PadService .pid - 1 }} "
1699- f" { 'DESCRIPTION' } " )
1700- print (Decore .invert (hdr ))
1780+ # This is the first usage of simple table. I assume this will be
1781+ # copied so I left a lot of comments. If you copy it feel free
1782+ # to be less verbose..
1783+ service_table = SimpleTable ([
1784+ Column ('NAME' , 15 , 'left' ), # Max service name length
1785+ Column ('STATUS' , 10 , 'left' ), # Max status text length (e.g., "running")
1786+ Column ('PID' , 7 , 'right' ), # Max PID digits
1787+ Column ('MEM' , 6 , 'right' ), # Max memory string (e.g., "123.4M")
1788+ Column ('UP' , 4 , 'right' ), # Max uptime string (e.g., "3d")
1789+ Column ('RST' , 3 , 'right' ), # Max restart count digits
1790+ Column ('DESCRIPTION' , 30 ) # Last column needs no padding
1791+ ])
1792+
1793+ print (service_table .header ())
17011794
17021795 for svc in services :
17031796 name = svc .get ('name' , '' )
17041797 status = svc .get ('status' , '' )
17051798 pid = svc .get ('pid' , 0 )
17061799 description = svc .get ('description' , '' )
1800+ stats = svc .get ('statistics' , {})
17071801
17081802 if status in ('running' , 'active' , 'done' ):
17091803 status_str = Decore .green (status )
@@ -1712,13 +1806,17 @@ def show_services(json):
17121806 else :
17131807 status_str = Decore .yellow (status )
17141808
1715- pid_str = str (pid ) if pid > 0 else '- '
1809+ pid_str = str (pid ) if pid > 0 else ' '
17161810
1717- row = f"{ name :<{PadService .name }} "
1718- row += f"{ status_str :<{PadService .status + 9 }} "
1719- row += f"{ pid_str :>{PadService .pid }} "
1720- row += f" { description } "
1721- print (row )
1811+ memory_bytes = int (stats .get ('memory-usage' , 0 ))
1812+ uptime_secs = int (stats .get ('uptime' , 0 ))
1813+ restart_count = stats .get ('restart-count' , 0 )
1814+
1815+ memory_str = format_memory_bytes (memory_bytes )
1816+ uptime_str = format_uptime_seconds (uptime_secs )
1817+
1818+ print (service_table .row (name , status_str , pid_str , memory_str ,
1819+ uptime_str , restart_count , description ))
17221820
17231821
17241822def show_hardware (json ):
0 commit comments