cli-tool

功能点:
1.调用动态库函数作为命令。如:get_portState,slot_present等。
2.调用可执行程序作为命令。如:iio-info,cpld-upgrade
3.满足tab键补全和上键补全历史记录

难点:
1.进程和线程同时使用stdin,需要加锁。本示例中锁为 stop_thread
2.实现命令的方式有三种,三种方式各不相同

  • 直接写
  • 调用动态库函数------lib = ctypes.CDLL(‘/usr/lib/libfpga.so’) ; lib.fpga_is_slot_present.[argtypes|restype]
  • 调用可执行文件------subprocess.Popen,采用这种方式,实现可执行文件实时的打印及输入

3.获取数据后对数据的解析

import click
import subprocess
import sys
import ctypes
import time
import threading
import shlex
import re
import os
import readline

stop_thread = threading.Event()

'''
c struct
typedef struct PORTSTATUSINFO
{
    char              portName[64];
    uint16_t          linkStatus;
}T_PORTSTATUSINFO;
'''
class T_PORTSTATUSINFO(ctypes.Structure):
    _fields_ = [
        ("portName",ctypes.c_char *64),
        ("linkStatus",ctypes.c_uint16)
    ]

lib = ctypes.CDLL('/usr/lib/libfpga.so') 

@click.group()
def cli():
    pass

history_index = 0
history = []



def custom_completer(text,state):
    commands = cli.commands
    options = [cmd.name for cmd in commands.values() if cmd.name.startswith(text)]
    if state < len(options):
        return options[state]  
    else:
        return None
    

readline.set_completer_delims('\t\n;') 
readline.set_completer(custom_completer)
readline.parse_and_bind('tab: complete')



def get_user_input(user_input):
    global history_index
    char = user_input[0]
    if char == '\x1b': 
        sequence = user_input[1] 
        if sequence == '[A':
            if history_index > 0:
                history_index -= 1
            return history[history_index] if history else ''
        elif sequence == '[B':
            if history_index < len(history) - 1 :
                history_index += 1
            return history[history_index] if history else ''
        elif sequence == '[C':
            pass
        elif sequence == '[D':
            pass
        else:
            return sequence
    elif char == '\n':
        history.append(readline.get_line_buffer())
        history.append(input)
        history_index = len(history)
        return user_input
    elif char == '\t':
        readline.insert_text(readline.get_completer().complete(readline.get_line_buffer()))
        readline.redisplay()
    else:
        readline.insert_text(user_input)
        return user_input

@cli.command()
@click.argument('input_file', type=click.Path(exists=True))
@click.argument('output_file', type=click.Path())
@click.option('--block-size', '-b', default=1024, help='Block size in bytes')
@click.option('--count', '-c', default=1, help='Number of blocks to copy')
def dd(input_file, output_file, block_size,count):
    """EMCC, SD read and write test commands."""
    if count < 0:
        count_arg = ''
    else:
        count_arg = 'count=' + str(count)
    cmd = ['dd', 'if=' + input_file, 'of=' + output_file, 'bs=' + str(block_size), count_arg,'status=progress']
    subprocess.run(cmd)


@cli.command()
@click.argument('help', default='')
def interrupt_count(help):
    """ Interrupt count query command"""
    try:
        if help == "help":
            click.echo("please input command [interrupt-count]")
        else:
            result = subprocess.run(['cat','/proc/interrupts'] , capture_output=True, text=True, check=True)
            click.echo(result.stdout)
    except subprocess.CalledProcessError as e:
        click.echo(f"Error:{e}", err=True)


@cli.command()
@click.argument('hd', type=str,default='')
@click.option('--name','-n',type=str,help='port name value:[PORT_NMS1,PORT_NMS1,PORT_ETH1,...]')
def get_portState(hd,name):
    '''EXI Ethernet Port Status Query Command'''
    try:
        if hd == 'help':
            click.echo('=========================get-portstate=========================\n')
            click.echo('Options:\n')
            click.echo('\t -n,\t--name\t\t\tport name\n')
            click.echo('Example:\n')
            click.echo('get-portstate -n name\n')
            click.echo('name = PORT_NMS ,PORT_NMS1 ,PORT_NMS2 ,PORT_ETH ,PORT_ETH1 ,PORT_ETH2\n')
        else:
            data = T_PORTSTATUSINFO()
            data.portName = name.encode('utf-8')
            data.linkStatus = 0xff

            lib.get_portState.argtypes = [ctypes.POINTER(T_PORTSTATUSINFO)]
            lib.get_portState.restype = ctypes.c_int

            result = lib.get_portState(ctypes.byref(data))
            if result != 0:
                click.echo(f"Error: get-portState failed with error code {result}")
            else:
                if data.linkStatus == 0:
                    click.echo(f"port {name} port status is link down")
                elif data.linkStatus == 1:
                    click.echo(f"port {name} port status is link up")
            
    except Exception as e:
        click.echo(f"Error:{e}")


@cli.command()
@click.argument('hd', type=str,default='')
@click.option('--id','-i',type=int,help='port name value:[1~8]')
def slot_present(hd,id):
    '''Command to query the status of each board in place'''
    try:
        present =ctypes.c_bool()
        present_ptr = ctypes.pointer(present)
        if hd == 'help':
            click.echo('=========================slot_present=======================\n')
            click.echo('Options:\n')
            click.echo('\t -i,\t--id\t\t\slot id\n')
            click.echo('Example:\n')
            click.echo('slot-present -i id\n')
            click.echo('id = 1~8\n')
        else:

            lib.fpga_is_slot_present.argtypes = [ctypes.c_int,ctypes.POINTER(ctypes.c_bool)]
            lib.fpga_is_slot_present.restype = ctypes.c_int

            result = lib.fpga_is_slot_present(id,present_ptr)
            if result != 0:
                click.echo(f"Error: slot_present failed with error code {result}")
            else:
                if present:
                    click.echo(f"slot{id} is ON\n")
                else:
                    click.echo(f"slot{id} is DOWN\n")
    except Exception as e:
        click.echo(f"Error:{e}")



def get_cu_temp(id):
    '''get_cu_temp '''
    try:
        temp =ctypes.c_float()
        temp_ptr = ctypes.pointer(temp)

        lib.get_cu_temp.argtypes = [ctypes.c_int,ctypes.POINTER(ctypes.c_float)]
        lib.get_cu_temp.restype = ctypes.c_int

        result = lib.get_cu_temp(id,temp_ptr)
        if result != 0:
            click.echo(f"Error: get_cu_temp failed with error code {result}")
        else:
            pass
        click.echo(f"cu temperature :" + str(temp.value) + "℃")
    except Exception as e:
        click.echo(f"Error:{e}")



def get_panel_temp():
    '''get_panel_temp '''
    try:

            lib.cpld_enable_panel_temperature_output.argtypes = [ctypes.c_int]
            lib.cpld_enable_panel_temperature_output.restype = ctypes.c_int

            result = lib.cpld_enable_panel_temperature_output(1)
            if result != 0:
                click.echo(f"Error: get_panel_temp failed with error code {result}")
            else:
                pass

            temp =ctypes.c_float()
            temp_ptr = ctypes.pointer(temp)

            lib.cpld_get_panel_board_temperature.argtypes = [ctypes.POINTER(ctypes.c_float)]
            lib.cpld_get_panel_board_temperature.restype = ctypes.c_int

            result = lib.cpld_get_panel_board_temperature(temp_ptr)
            if result != 0:
                click.echo(f"Error: get_panel_temp failed with error code {result}")
            else:
                pass
            click.echo(f"panel temperature :" + str(temp.value) + "℃")
    except Exception as e:
        click.echo(f"Error:{e}")



def get_fan_temp(index):
    '''get_fan_temp '''
    try:
            lib.cpld_enable_fan_temperature_output.argtypes = [ctypes.c_int]
            lib.cpld_enable_fan_temperature_output.restype = ctypes.c_int

            result = lib.cpld_enable_fan_temperature_output(1)
            if result != 0:
                click.echo(f"Error: get_fan_temp failed with error code {result}")
            else:
                pass

            temp =ctypes.c_float()
            temp_ptr = ctypes.pointer(temp)

            lib.cpld_get_fan_board_temperature.argtypes = [ctypes.c_int,ctypes.POINTER(ctypes.c_float)]
            lib.cpld_get_fan_board_temperature.restype = ctypes.c_int

            result = lib.cpld_get_fan_board_temperature(index,temp_ptr)
            if result != 0:
                click.echo(f"Error: get_fan_temp failed with error code {result}")
            else:
                pass
            click.echo(f"fan tempeture :" + str(temp.value) + "℃")
    except Exception as e:
        click.echo(f"Error:{e}")




@cli.command()
@click.argument('card', type=str,default='')
@click.option('--id','-i',type=int,help='cu_41 : -i 0 ; cu_42 : -i 1')
def get_temp(card,id):
    '''Temperature query command'''
    try:
        if card == 'help':
            click.echo('=========================get_cu_temp=========================\n')
            click.echo('Options:\n')
            click.echo('\t -i,\t--id\t\t\tcu/fan/psu id\n')
            click.echo('Example:\n')
            click.echo('panel temp: get-temp panel \n')
            click.echo('cu41 temp: get-temp cu -i 0 \n')
            click.echo('cu42 temp: get-temp cu -i 1 \n')
            click.echo('fan#1 temp: get-temp fan -i 1 \n')
            click.echo('fan#2 temp: get-temp fan -i 2 \n')
            click.echo('fan#3 temp: get-temp fan -i 3 \n')
            click.echo('psu#1 temp: get-temp psu -i 1 \n')
            click.echo('psu#2 temp: get-temp psu -i 2 \n')

        elif card == 'panel':
            get_panel_temp()
        elif card == 'fan':
            if id > 0 and id < 4:
                get_fan_temp(id)
            else:
                click.echo('id :1~3')
        elif card == 'cu':
            if id == 0 or id == 1 :
                get_cu_temp(id)
            else:
                click.echo('id :0~2')
        elif card ==  'psu':
            if id == 1 or id == 2 :
                ctx = click.get_current_context()
                ctx.invoke(psu, iardtype=str(id), temperature='-t')
            else:
                click.echo('id :1~2')
        else:
            click.echo('Please input [panel,cu ,fan, psu]')
    except Exception as e:
        click.echo(f"Error:{e}")



@cli.command()
@click.argument('bus', type=str,default='')
@click.argument('obj', type=str,default='')
@click.argument('objfront', type=str,default='')
@click.argument('objbehind', type=str,default='')
@click.argument('opfront', type=str,default='')
@click.argument('opmiddle', type=str,default='')
@click.argument('opbehind', type=str,default='')
def mdio(bus,obj,objfront,objbehind,opfront,opmiddle,opbehind):
    '''SMI read and write commands'''
    try:
        command=['/usr/bin/mdio']
        if bus == 'help':
            result = subprocess.run(['/usr/bin/mdio','-h'],capture_output=True, text=True, check=True)
        else:
            if bus != '':
                command.append(bus)
            if obj != '':
                command.append(obj)
            if objfront != '':
                command.append(objfront)
            if objbehind != '':
                command.append(objbehind)
            if opfront != '':
                command.append(opfront)
            if opmiddle != '':
                command.append(opmiddle)
            if opbehind != '':
                command.append(opbehind)
            result = subprocess.run(command,capture_output=True, text=True, check=True)
        click.echo(result.stdout.strip())
    except subprocess.CalledProcessError as e:
        click.echo(f'Error: {e}')



@cli.command()
@click.argument('hd', type=str,default='')
@click.option('--version','-v','version', type=str, help='bus id',flag_value='-v')
@click.option('--iardtype','-i', type=str, help='device address',default='3')
@click.option('--ktype','-k', type=str, help='register address',default='3')
@click.option('--protect','-p','protect', type=str, help='num of register',flag_value='-p')
@click.option('--temperature','-t', 'temperature',type=str, help='register address bandwith',flag_value='-t')
@click.option('--file','-f','file', type=str, help='value',flag_value='-f')
@click.option('--alarm','-a','alarm', type=str, help='value',flag_value='-a')
@click.option('--manufacture','-m','manufacture', type=str, help='value',flag_value='-m')
@click.option('--read','-r','read', type=str, help='value',flag_value='-r')
def psu(hd,version,iardtype,ktype,protect,temperature,file,alarm,manufacture,read):
    ''' psu'''
    try:
        command=['/usr/bin/psu']
        if version == '-v':
            command.append('-v')
        if iardtype != '3':
            command.append('-i') 
            command.append(iardtype)
        if ktype != '3':
            command.append('-k') 
            command.append(ktype)
        if protect == '-p':
            command.append('-p')
        if temperature == '-t':
            command.append('-t')
        if file == '-f':
            command.append('-f')
        if alarm == '-a':
            command.append('-a')
        if manufacture == '-m':
            command.append('-m')
        if read == '-r':
            command.append('-r')
        if hd == 'help':
            result = subprocess.run(['/usr/bin/psu','--help'],capture_output=True, text=True, check=True)
        else:
            result = subprocess.run(command,capture_output=True, text=True, check=True) 
        click.echo(result.stdout.strip())
    except subprocess.CalledProcessError as e:
        click.echo(f'Error: {e}')



@cli.command()
@click.argument('hd', type=str,default='')
@click.option('-w', 'opera', flag_value='-w')
@click.option('-r', 'opera', flag_value='-r')
@click.option('-g', 'opera', flag_value='-g')
@click.option('--slot','-s', type=str,help='slot id')
@click.option('--file','-f', type=click.Path(exists=True),help='filename')
@click.option('--offset','-o', type=str,help='offset')
@click.option('--length','-l', type=str,help='lenght')
def inventory(hd,opera,slot,file,offset,length):
    '''EEPROM read and write commands. {panel|fan|cu|exi|psu}'''
    try:
        if hd == 'help':
            result = subprocess.run(['/usr/bin/inventory', '--help'],capture_output=True, text=True, check=True)
            click.echo(result.stdout.strip())
        elif opera.lower() == '-w':
            if slot in ['cu41','cu42']:
                result = subprocess.run(['/usr/bin/fpga_tool', str('-w'), str('-a'),str('0x18') ,str('-v'), str('0x0')], capture_output=True, text=True, check=True) 
                result = subprocess.run(['/usr/bin/inventory', str('-w'), str('-s'),slot, str('-f'), file], capture_output=True, text=True, check=True)
                result = subprocess.run(['/usr/bin/fpga_tool', str('-w'), str('-a'), str('0x18'), str('-v') ,str('0x1')], capture_output=True, text=True, check=True)
            elif slot == 'slot40' or slot == 'fan31' or slot == 'fan32' or slot == 'fan33':
                address = '0x0c'
                value = '0x1'
                bus = '0x3'
                if slot == 'slot40':
                    device = '0x60'
                    address = '0x04'
                    value = '0x10'
                    bus = '0x2'
                elif slot == 'fan31':
                    device = '0x64'
                elif slot == 'fan32':
                    device = '0x65'
                elif slot == 'fan33':
                    device = '0x66'
                result = subprocess.run(['/usr/bin/fpga_i2c', str('-w'), str('-b'), bus, str('-d'), device, str('-a'), address, str('-p'), str('2'), str('-v'), str('0x0')], capture_output=True, text=True, check=True) 
                result = subprocess.run(['/usr/bin/inventory', str('-w'), str('-s'), slot ,str('-f'), file], capture_output=True, text=True, check=True)
                result = subprocess.run(['/usr/bin/fpga_i2c', str('-w'), str('-b'), bus, str('-d'), device ,str('-a') ,address, str('-p') ,str('2') ,str('-v'), value], capture_output=True, text=True, check=True)
            elif slot == 'slot1~8':
                '''line card,  cpld_tool'''
                pass
            elif slot == 'slot0' or slot == 'psu21' or slot == 'psu22' or slot == 'mux1' or slot == 'mux2' or slot == 'mux3' or slot == 'mux4':
                result = subprocess.run(['/usr/bin/inventory',str('-w'), str('-s'), slot ,str('-f'), file], capture_output=True, text=True, check=True)
        elif opera.lower() == '-g':
            result = subprocess.run(['/usr/bin/inventory',str('-g'),str('-s'), slot],capture_output=True, text=True, check=True)
            click.echo(result.stdout.strip())
        elif opera.lower() == '-r':
            result = subprocess.run(['/usr/bin/inventory',str('-r'),str('-s'), slot,str('-o'), offset,str('-l'),length],capture_output=True, text=True, check=True)
            click.echo(result.stdout.strip())
    except subprocess.CalledProcessError as e:
        click.echo(f'Error: {e}')




@cli.command()
@click.argument('hd', type=str,default='')
@click.option('--bus','-b', type=str, help='bus id',default='')
@click.option('--device','-d', type=str, help='device address')
@click.option('--address','-a', type=str, help='register address')
@click.option('--num','-n', type=str, help='num of register',default='')
@click.option('--bandwidth','-p', type=str, help='register address bandwith',default='')
@click.option('-w', 'opera', flag_value='-w')
@click.option('-r', 'opera', flag_value='-r')
@click.option('--value','-v', type=str,multiple=True, help='value',default='')
def fpga_i2c(hd,bus,device,address,num,bandwidth,opera,value):
    '''I2C read and write commands'''
    try:
        value_list = list(value)
        command=['/usr/bin/fpga_i2c',str('-b'), bus, str('-d') ,device ,str('-a'), address ,str('-n'), num ,str('-p'), bandwidth, str(opera)]
        if hd == 'help':
            result = subprocess.run(['/usr/bin/fpga_i2c', str('--help')],capture_output=True, text=True, check=True)
        elif opera == '-w':
            for i in range(int(num)):
                command += [str('-v'),value_list[int(i)]]
            result = subprocess.run(command,capture_output=True, text=True, check=True) 
        elif opera == '-r':
            result = subprocess.run(['/usr/bin/fpga_i2c',str('-b'), bus, str('-d') ,device ,str('-a'), address ,str('-n'), num ,str('-p'), bandwidth, str(opera)],capture_output=True, text=True, check=True) 
        click.echo(result.stdout.strip())
    except subprocess.CalledProcessError as e:
        click.echo(f'Error: {e}')


@cli.command()
@click.argument('hd', type=str,default='')
@click.option('--fan','-f', type=str, help='fan1 | fan2 | fan3',default='')
@click.option('--opera','-o', type=str, help='read/low/middle/height',default='')
def fan_speed(hd,fan,opera):
    '''fan-speed'''
    fan_address = ['0x64','0x65','0x66']
    fan_id = ['fan1','fan2','fan3']
    ctx = click.get_current_context()
    if opera == 'read' :
        ctx.invoke(fpga_i2c,  bus='3',device=fan_address[fan_id.index(fan)], address='0x80' , num='4', bandwidth='2', opera='-r')
    elif opera == 'middle':
        ctx.invoke(fpga_i2c,  bus='3',device=fan_address[fan_id.index(fan)], address='0x80' , num='4', bandwidth='2', opera='-w',value=('0x5b', '0x8f', '0xcc', '0xcc'))
    elif opera == 'low':
        ctx.invoke(fpga_i2c,  bus='3',device=fan_address[fan_id.index(fan)], address='0x80' , num='4', bandwidth='2', opera='-w',value=('0x70', '0x3d', '0xcc', '0xcc'))
    elif opera == 'height':
        ctx.invoke(fpga_i2c,  bus='3',device=fan_address[fan_id.index(fan)], address='0x80' , num='4', bandwidth='2', opera='-w',value=('0xcc', '0xcc', '0xcc', '0xcc'))
    if hd == 'help':
        click.echo('please input fan-speed -f [fan1 | fan2 | fan3] -o [read | low | middle | height]')

@cli.command()
@click.argument('hd', type=str,default='')
@click.option('-w', 'opera', flag_value='-w')
@click.option('-r', 'opera', flag_value='-r')
@click.option('--address','-a', type=str, help='address')
@click.option('--num','-n', type=str, help='num of register',default='')
@click.option('--value','-v', type=str, help='value',default='')
def fpga_tool(hd,opera,address,num,value):
    ''' reset sub card, CU main/backup, board in place'''
    try:
        if hd == 'help':
            result = subprocess.run(['/usr/bin/fpga_tool', str('--help')],capture_output=True, text=True, check=True)
        else:
            result = subprocess.run(['/usr/bin/fpga_tool', str('-a') ,address, opera ,str('-n'),num,str('-v'),value],capture_output=True, text=True, check=True)
            print(hd)
        click.echo(result.stdout.strip())
    except subprocess.CalledProcessError as e:
        click.echo(f'Error: {e}')

@cli.command()
@click.argument('opera', type=str, default='')
def get_id(opera):
    '''get_id'''
    if opera == 'help':
        click.echo("please input command : get-id\n")
    else:
        ctx = click.get_current_context()
        ctx.invoke(fpga_tool, opera='-r', address='0x13',num='5')
    
    

@cli.command()
@click.argument('opera', type=str, default='')
def watch_dog(opera):
    '''cu ? fan watch_dog'''
    ctx = click.get_current_context()
    if opera == 'read' :
        ctx.invoke(fpga_tool, opera='-r', address='0x0d')
    elif opera == 'enable':
        ctx.invoke(fpga_tool, opera='-w', address='0x0d',value='1')
    elif opera == 'disabled':
        ctx.invoke(fpga_tool, opera='-w', address='0x0d',value='0')
    else:
        click.echo('please input watch-dog [read | enable | disabled ]')


@cli.command()
@click.argument('hd', type=str,default='')
@click.option('--slot','-s', type=str, help='slot1~slot8',default='')
@click.option('--opera','-o', type=str, help='put on/off',default='')
def card_power(hd,slot,opera):
    '''card_power'''
    slot_id = ['slot1','slot2','slot3','slot4','slot5','slot6','slot7','slot8']
    slot_address = ['0x61','0x62','0x63','0x64','0x65','0x66','0x67','0x68']
    
    ctx = click.get_current_context()
    if opera == 'on' :
        ctx.invoke(fpga_tool, opera='-w', address=slot_address[slot_id.index(slot)],value='0x10')
        ctx.invoke(fpga_tool, opera='-w', address=slot_address[slot_id.index(slot)],value='0x50')

        ctx.invoke(fpga_tool, opera='-w', address=slot_address[slot_id.index(slot)],value='0x18')
        ctx.invoke(fpga_tool, opera='-w', address=slot_address[slot_id.index(slot)],value='0x50')

        ctx.invoke(fpga_tool, opera='-r', address=slot_address[slot_id.index(slot)],num='1')
    elif opera == 'off':
        ctx.invoke(fpga_tool, opera='-w', address=slot_address[slot_id.index(slot)],value='0x14')
        ctx.invoke(fpga_tool, opera='-w', address=slot_address[slot_id.index(slot)],value='0x50')

        ctx.invoke(fpga_tool, opera='-w', address=slot_address[slot_id.index(slot)],value='0x1c')
        ctx.invoke(fpga_tool, opera='-w', address=slot_address[slot_id.index(slot)],value='0x50')

        ctx.invoke(fpga_tool, opera='-r', address=slot_address[slot_id.index(slot)],num='1')
    if hd == 'help':
        click.echo('please input command: card-power -s [slot1~slot8] -o [on | off ]')

@cli.command()
@click.argument('opera', type=str, default='')
def master_backup(opera):
    '''master_backup'''
    ctx = click.get_current_context()
    if opera == 'read' :
        ctx.invoke(fpga_tool, opera='-r', address='0x0f')
    elif opera == 'master':
        ctx.invoke(fpga_tool, opera='-w', address='0x10',value='0x6')
    elif opera == 'backup':
        ctx.invoke(fpga_tool, opera='-w', address='0x10',value='0x5')
    else:
        click.echo('please input command: master-backup [read | master | backup ]')


@cli.command()
@click.argument('opera', type=str, default='')
def reset_fan(opera):
    '''reset_fan'''
    if opera =='':
        ctx = click.get_current_context()
        ctx.invoke(fpga_tool, opera='-w', address='0x1f',value='0xa55a')
        ctx.invoke(fpga_tool, opera='-w', address='0x05',value='0x0')
    else:
        click.echo(' Execute command: [reset-fan] will reset three fans ')

@cli.command()
def exit():
    """Exit from current program."""

@cli.command()
def help():
    """Show help for all commands."""
    commands = cli.commands
    max_length = max(len(command.help) for command in commands.values())
    click.echo(f"\nCommands available:\n")
    for command in commands.values():
        click.echo(f"{command.name.ljust(20)}\t\t\t{command.help.ljust(max_length)}")


def iio_result(process):
    adc = []
    ads = []
    i=0
    while True:
        output = process.stdout.readline()
        #click.echo(output)
        if output == '' and  process.poll() is not None:
            break
        if 'raw value:' in output:
            pattern = re.compile(r'raw value:\s*(\d+)')
            num = pattern.findall(output)
            
            if i<8:
                adc.append(num[0])
            else:
                ads.append(num[0])
            i += 1
    click.echo("analysis result:\r\n")
    click.echo("rk3568 adc\r\n")
    i=0
    adc_result = [int(item)*1.8/1023 for item in adc]
    for num in adc_result:
        click.echo(f"channels[{i}] {num:.3f}V\r\n")
        i+=1
    if os.path.exists('/sys/class/gpio/gpio23'):
        result = subprocess.run(['sudo cat /sys/class/gpio/gpio23/value'],shell = True,stdout=subprocess.PIPE)
        num = result.stdout.decode().strip()
        click.echo(f"ads7959 {num}\r\n")
    else:
        click.echo("ads7959\r\n")
    i=0
    ads_result = [int(item)*2.5/255 for item in ads]
    for num in ads_result:
        click.echo(f'channels[{i}] {num:.3f}V\r\n')
        i+=1

def gpio_switch(id):
    result = subprocess.run(['sudo echo 23 > /sys/class/gpio/export'],shell = True,stdout=subprocess.PIPE)
    result = subprocess.run(['sudo echo out > /sys/class/gpio/gpio23/direction'],shell = True,stdout=subprocess.PIPE)
    result = subprocess.run([f'sudo echo {id} > /sys/class/gpio/gpio23/value'],shell = True,stdout=subprocess.PIPE)
    #result = subprocess.run(['sudo cat /sys/class/gpio/gpio23/value'],shell = True,stdout=subprocess.PIPE)



def iio_output(process):
    try:

        iio_result(process)
        if os.path.exists('/sys/class/gpio/gpio23'):
            result = subprocess.run(['sudo echo 23 > /sys/class/gpio/unexport'],shell = True,stdout=subprocess.PIPE)
            #click.echo(f"select value: {result}\r\n")
        stop_thread.set()
            
        #time.sleep(0.1)
    except KeyboardInterrupt:
        click.echo('Process interrupted by user')
        stop_thread.set()

def direct_output(process):
    try:
        while not stop_thread.is_set():
            output = process.stdout.readline()
            if output == '' and  process.poll() is not None:
                stop_thread.set()

            if 'error' in output.lower():
                stop_thread.set()     
            
            if output:
                click.echo(output, nl=False) 
                if 'input' in output:
                    usr_input = click.prompt("Input your selection ",type=str).strip()
                    process.stdin.write(usr_input + '\n')
                    process.stdin.flush()
                sys.stdout.flush() 
            
        #time.sleep(0.1)
    except KeyboardInterrupt:
        click.echo('Process interrupted by user')
        stop_thread.set()


@cli.command()
def iio_info():
    """Voltage measurement result query command."""
    click.echo('\n')

@cli.command()
def cpld_upgrade():
    """CPLD Upgrade Command."""
    click.echo('\n')


def extract_and_convert_numbers(s):
    hex_numbers = re.findall(r'0x([0-9a-fA-F]+)', s)
    numbers = []
    for hex_num in hex_numbers:
        try:
            number = int(hex_num, 16)
            numbers.append(number)
        except ValueError:
            print(f"Unable to convert hexadecimal numbers: {hex_num}")
    return numbers

def update_reault(result):
    #click.echo(result + "\n")
    slot_address = ['0x61','0x62','0x63','0x64','0x65','0x66','0x67','0x68']
    index = result.lower().find("successfully")

    if index != -1:
        if any(slot in result for slot in slot_address) or '0x10' in result \
             or '0x80' in result or '0xd' in result:
            pass
        elif '0x5' in result:
            click.echo("reset fan successfully\n")
        else:
            click.echo(result[index+len("successfully"):])
    elif result.find("====") != -1 and result.find("temperature:") != -1:
        result = result.replace("\n", "℃\n")
        click.echo(result[index+len("====="):])
        #click.echo("℃\n")
    
    elif 'Read' in result :
        if any(slot in result for slot in slot_address):
            if '0x80' in result:
                number = extract_and_convert_numbers(result)
                speed =''
                if number[6] == 91:
                    speed ='middle'
                elif number[6] == 112:
                    speed ='low'
                elif number[6] == 204:
                    speed ='height'
                else:
                    speed = 'default'
                click.echo(f"fan speed is {speed} \n")
            else:
                number = extract_and_convert_numbers(result)
                click.echo(f"slot power status is {'on' if number[1] == 466 else 'off'}\n")
        elif '0xd' in result:
            number = extract_and_convert_numbers(result)
            click.echo(f"watch dog is {'enable' if number[1] == 1 else 'disabled'} \n")
        elif '0x13' in result:
            name_id =['CU-SLOT-ID','SHELF-ID','HARDWARE-ID','PCB-ID','BOM-ID']
            number = extract_and_convert_numbers(result)
            max_length = max(len(str(index)) for index in number)
            for index in range(5):
                click.echo('\n\t' + name_id[index].ljust(15) + f':\t{str(hex(number[index+1])).ljust(max_length)}' +'\n')
        elif '0xf' in result:
            if 'mdio' in result:
                click.echo(result)
            else:
                number = extract_and_convert_numbers(result)
                click.echo(f"cu status is {'master' if number[1] == 1 else 'backup'} \n")
        elif 'exit' in result:
            click.echo(result)
    else:
        click.echo(result)


def execute_command(command_string):
    from click.testing import CliRunner

    runner = CliRunner()
    result = runner.invoke(cli, command_string.split())
    #click.echo(command_string.split())
    if result.exit_code == 0:
        #click.echo(result.output)
        update_reault(result.output)
    else:
        click.echo(f'Error: {result.output}')
    

def run_command(command_args):
    try:
        if "cpld-upgrade"  in command_args or \
            "iio-info" in command_args:
            stop_thread.clear()
            if 'cpld-upgrade' in command_args:
                
                replaced_string = command_args.replace("cpld-upgrade", "/usr/bin/cpld_upgrade")
            elif 'iio-info' in command_args:
                if 'ads7959' in command_args:
                    gpio_switch(command_args[17])
                    command_args = command_args.replace(f"ads7959 {command_args[17]}","")
                replaced_string = command_args.replace("iio-info", "/usr/bin/iio_info")
                
            if 'help' in replaced_string:
                replaced_string = replaced_string.replace("help", "--help")
            command_args = shlex.split(replaced_string)
            process = subprocess.Popen(
                command_args,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                stdin=subprocess.PIPE,
                text=True,
                bufsize=1,
                universal_newlines=True,
            )
            if '/usr/bin/iio_info' in process.args and '--help' not in process.args :
                output_thread = threading.Thread(target=iio_output, args=(process,))
                output_thread.start()
            else:
                output_thread = threading.Thread(target=direct_output, args=(process,))
                output_thread.start()

            if stop_thread.is_set():
                output_thread.join()
                
                if process.poll() is None:
                    process.terminate()
                    try:
                        process.wait(timeout=5)
                    except subprocess.TimeoutExpired:
                        click.echo('Child process timed out, forced termination.')
                        process.kill() 
                process.stdout.close()
                process.stderr.close()
                process.stdin.close()
                process.communicate()
                click.echo('\r\n')
        elif command_args == '':
            pass
        else:
            execute_command(command_args)
            
    except Exception as e:
        click.echo(f'Exception: {str(e)}')


def interactive_mode():
    stop_thread.set()
    user_input = ''
    while True:
        while stop_thread.is_set():
            user_input = input('>>>: ')
            if user_input.lower() == 'exit':
                click.echo('Exiting Interactive CLI. Goodbye!')
                break
            else:
                run_command(user_input)
        if user_input.lower() == 'exit':
            break

if __name__ == '__main__':
    interactive_mode()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值