import struct
import sqlite3
import time
import sys
import matplotlib.pyplot as plt
import numpy as np
import argparse
#import Exceptions
from pymodbus.client import ModbusSerialClient as ModbusClient
#from pymodbus.exceptions import ModbusInvalidResponseError
import random # for test
from datetime import datetime
from matplotlib import dates

# 打开串口
client = ModbusClient(method="rtu", port='COM1', baudrate=9600)
client.connect()

# args
def usage():
    print('''
    -h, --help: 帮助
    -m, --measure: 读取测量值
    -w, --warning: 读取继电器报警
    -s, --setting: 写寄存器
    -r, --read: 读取寄存器
    -mo, --monitor: 持续读取测量值并记录
    -o, --output: 输出文件名
    ''')

### 寄存器操作
def read_register(reg_addr, reg_count) -> int:
    # 读取寄存器并返回值
    rr = client.read_holding_registers(reg_addr, reg_count,unit=0x01)
    ans = rr.registers[0]
    return ans

def read_float(register_addr) -> int:
    # 读取寄存器并转换为浮点数
    # count: 2
    ans = read_register(register_addr,2)
    result = struct.unpack('>f', struct.pack('>HH', ans))[0]
    return result

def write_register(register_addr, value) -> int:
    # 往一个寄存器写入值
    result = client.write_register(register_addr, value, unit=0x01)
    return result

def write_float(register_addr, value) -> int:
    # 往一个寄存器写入浮点数
    val = struct.unpack('>HH', struct.pack('>f', value))
    result = client.write_registers(register_addr, val, unit=0x01)
    return result

### 测量操作
def measure_float() -> float:
    # 读取测量值浮点数
    # addr: 0x20
    # count: 2
    ans = read_float(0x20)
    return ans.__round__(4)

### 读取操作
def read_current_ct():
    # 读取电流变比
    # addr: 0x1C
    # count: 1
    ans = read_register(0x1C, 1)
    return ans

def read_comm_addr():
    # 读取通讯地址
    # addr: 0x1B
    # count: 1
    ans = read_register(0x1B, 1)
    return ans

def read_comm_bin():
    # 读取通讯比特率
    # addr: 0x1C 
    # count: 1
    ans = read_register(0x1C, 1)
    return ans

def read_dp():
    # 小数点位置
    # addr: 0x1E 
    # count: 1
    ans = read_register(0x1E, 1)
    dp = ans.registers[0]
    return bin(dp)[-1]

def measure_int():
    # DM5 不支持
    pass

def read_war_uperhigh():
    # 读取上上限
    # addr: 0x20 
    ans = read_float(0x20)
    return ans.__round__(4)

def read_war_high():
    # 读取上限
    # addr: 0x22 
    ans = read_float(0x22)
    return ans.__round__(4)

def read_out_delay():
    # 读取输出延时
    # addr: 0x24 
    ans = read_float(0x24)
    return ans.__round__(4)

def read_war_low():
    # 读取下限
    # addr: 0x26 
    ans = read_float(0x26)
    return ans.__round__(4)

def read_war_uperlow():
    # 读取下下限
    # addr: 0x28 
    ans = read_float(0x28)
    return ans.__round__(4)

def read_out_offset():
    # 读取输出偏移
    # addr: 0x2C 
    ans = read_float(0x2C)
    return ans.__round__(4)

def read_warning():
    # 继电器报警
    # addr: 0x2E 
    war = read_register(0x2E, 2)
    war = client.read_holding_registers(0x002E, 2, unit=0x01).registers[0]
    war_HH = int(war & 0b1111000000000000) >> 12 #上上限
    war_HI = int(war & 0b0000111100000000) >> 8 #上限
    war_GO = int(war & 0b0000000011110000) >> 4
    war_LO = int(war & 0b0000000000001111) #下限
    war_LL = int(war & 0b1111000000000000) >> 12 #下下限
    return war_HH, war_HI, war_GO, war_LO, war_LL

### 写入操作
def setting(current_ct=None, comm_addr=None, comm_bin=None, war_uperhigh=None, war_high=None, war_uperlow=None, war_low=None, out_delay=None, out_offset=None):
    # 配置
    ### Int
    # current_ct: 电流变比CT   0x14 1
    # comm_addr: 通讯地址   0x1B 1
    # comm_bin: 通讯比特率   0x1C 1
    ### Float
    # war_uperhigh: 上上限   0x20 2
    # war_high: 上限    0x22 2
    # war_uperlow: 下下限  0x28 2
    # war_low: 下限     0x26 2
    # out_delay: 输出延时   0x24 2
    # out_offset: 输出偏移  0x2C 2
    # 写入电流变比触发器 CT 值
    if current_ct is not None: write_register(0x14, current_ct)
    # 写入通讯地址
    if comm_addr is not None: write_register(0x1B, comm_addr)
    # 写入通讯比特率
    if comm_bin is not None: write_register(0x1C, comm_bin)
    # 写入上上限
    if war_uperhigh is not None: write_float(0x20, war_uperhigh)
    # 写入上限
    if war_high is not None: write_float(0x22, war_high)
    # 写入下下限
    if war_uperlow is not None: write_float(0x28, war_uperlow)
    # 写入下限
    if war_low is not None: write_float(0x26, war_low)
    # 写入输出延时
    if out_delay is not None: write_float(0x24, out_delay)
    # 写入输出偏移
    if out_offset is not None: write_float(0x2C, out_offset)

### 记录操作
def record(filename):
    # 创建数据库
    conn = sqlite3.connect(filename)
    cursor = conn.cursor()
    cursor.execute('CREATE TABLE IF NOT EXISTS data(id INTEGER PRIMARY KEY AUTOINCREMENT,data FLOAT,warning INT,date DATE,time TIME,timestamp TIMESTAMP)')
    # 记录
    try:
        # 持续记录数据
        while True:
            # 读取寄存器
            result = measure_float()
            warnings = read_warning()
            if any(warnings):
                warning = warnings.index(1)
            else:
                warning = 0
            # 存储数据
            date = time.strftime("%Y-%m-%d", time.localtime())
            current_time = time.strftime("%H:%M:%S", time.localtime())
            timestamp = int(time.time())
            cursor.execute('INSERT INTO data(data, warning, date, time, timestamp) VALUES(?, ?, ?, ?, ?)', (result, warning, date, current_time, timestamp))
            conn.commit()

            # 打印数据
            print(f"Received data: {result}, Date: {date}, Time: {current_time}")

            # 等待
            time.sleep(0.1)
    except Exception as e:
        print(e)
    finally:
        # 关闭串口和数据库连接
        client.close()
        cursor.close()
        conn.close()

def gen_random_data():
    yield random.uniform(0, 100)

### 监控操作
def monitor():
    fig, ax = plt.subplots()
    x = []
    y = []
    line, = ax.plot(x,y)
    plt.ion()
    plt.show()
    try:
        while True:
            #result = measure_float()
            result = next(gen_random_data())
            warnings = read_warning()
            date = time.strftime("%Y-%m-%d", time.localtime())
            current_time = datetime.now()
            print(f"Received data: {result}, Date: {date}, Time: {current_time}")
            if any(warnings):
                print(warnings.index(1)) # 显示上限警告
            # 实时绘图
            x.append(current_time)
            y.append(result)
            if len(x) > 50:
                x = x[-50:]
                y = y[-50:]
            line.set_xdata(dates.date2num(x))
            line.set_ydata(y)
            ax.relim()
            ax.autoscale_view(1,1,1)
            plt.pause(0.1)
    except Exception as e:
        print(e)
    finally:
        client.close()

if __name__ == "__main__":

    parser = argparse.ArgumentParser(description='Modbus device record utility for M5')
    parser.add_argument('-m', '-measure', action='store_true', help='measure for one time')
    parser.add_argument('-w', '-warning', action='store_true', help='read the relay warning')
    parser.add_argument('-s', '-setting', nargs='+' , help='write specific registers')
    parser.add_argument('-r', '-read', nargs='+', type=str, help='read registers')
    parser.add_argument('-mo', '-monitor', action='store_true', help='monitor the measure data')
    parser.add_argument('-o', '-output', type=str, dest='filename', help='if provided, save the data to the file')

    args = parser.parse_args()
    print(args)
    if args.m:
        print("Value:",measure_float())
    if args.w:
        print('Warning:',read_warning())
    if args.s:
        settings = {}
        for arg in args.s:
            k,v = arg.split('=')
            settings[k] = v
        setting(**settings)
    if args.r:
        for arg in args.r:
            print(f'Register {arg}:',read_register(int(arg,16)))
    if args.mo:
        if args.filename:
            print(f'Output filename: {args.filename}')
            record(args.output)
        else:
            monitor()
    else:
        parser.print_help()

    这是一个使用modbus协议与下位机沟通的代码，你有什么优化建议和修改建议吗？