我根据老师的视频课程程序创建了一个应用程序。事实证明我遇到了一些问题,给我返回了以下异常,我非常感谢您的关注:
Error during data status reading.
Error during data setpoint reading.
Error during data tempOven reading.
Error during data tempOven reading.
Error during data tempOven reading.
我知道这是一个理论脚本,但如果你们可以在计算机上运行,我会很感激,因为我手工复制了老师的脚本,但它根本不起作用,尽管它没有错误。只要我在这里发布代码,我就会继续在这里写它的概述,并且文件将是 3 个(kivyMD .kv、main.py 和 datacards)。应用程序的外观是这样的: 这些问题恰好在下面的页面上触发(数据采集),因为连接步骤成功。
从主程序开始,此代码实例化 MDApp,然后实例化 Screen。它有一个按钮,如图所示,用于连接。连接方法本身实例化时钟调度程序,以最终更新保持卡和线圈卡。
from kivymd.app import MDApp
from kivymd.uix.screen import MDScreen
from datacard import CardCoil, CardHoldingRegister, CardInputRegister
from pyModbusTCP.client import ModbusClient
from kivymd.uix.snackbar import Snackbar
from kivy.clock import Clock
class MyWidget(MDScreen):
"""
Contructor
"""
def __init__(self,tags,**kwargs):
self._clientMOD= ModbusClient()
super().__init__(**kwargs)
self._tags=tags
self._ev=[] #pt4 min 58
for tag in self._tags:
if tag["type"]=="input":
self.ids.modbus_data.add_widget(CardInputRegister(tag,self._clientMOD))
elif tag["type"]== "holding":
self.ids.modbus_data.add_widget(CardHoldingRegister(tag,self._clientMOD))
elif tag["type"]=="coil":
self.ids.modbus_data.add_widget(CardCoil(tag,self._clientMOD))
def connect(self):
if self.ids.bt_con.text== "CONNECT":
try:
self.ids.bt_con.text= "DISCONNECT"
self._clientMOD.host= self.ids.hostname.text
self._clientMOD.port = int(self.ids.port.text)
self._clientMOD.open()
Snackbar(text="Connected succesfully",bg_color=(0,1,0,1)).open()
self._ev=[]
for card in self.ids.modbus_data.children:
if card.tag['type'] == "holding" or card.tag['type']== "coil":
self._ev.append(Clock.schedule_once(card.update_card))
else:
self._ev.append(Clock.schedule_interval(card.update_card,1))
except Exception as e:
print("Erro de conexao com servidor: ", e.args)
else:
self.ids.bt_con.text="CONNECT"
for event in self._ev:
event.cancel()
self._clientMOD.close()
Snackbar(text="Disconnected",bg_color=(1,0,0,1)).open()
class BasicApp(MDApp):
__tags= [{'name':'tempOven','description':'Oven Tempture','unit':'ºC','address':1000,'type':"input"},
{'name':'setpoint','description':'Desired Tempture','unit':'ºC','address':2000,'type': "holding"},
{'name':'status','description':'Actuator state','address':1000,'type':"coil"},
]
def build(self):
self.theme_cls.primary_palette= "Orange"
self.theme_cls.primary_hue= "700"
self.theme_cls.accent_palette="Orange"
return MyWidget(self.__tags)
if __name__=='__main__':
BasicApp().run()
第二个文件和下一步是数据卡模块。其目标是定义 2 个类(MDcard 类)和 3 个不同的 MDcard 子类。实际上,这三者代表了 Mbus 功能保持、输入或线圈之间的方法差异。他们的目标是与服务器交互:
from kivymd.uix.card import MDCard,MDCard
from pyModbusTCP.client import ModbusClient
class DataCard(MDCard):
title= "Data Card"
def __init__(self,tag,mbusClient,**kwargs):
self.tag= tag
self.title=self.tag['description']
self._mbusclient = mbusClient
super().__init__(**kwargs)
def update_card(self,dt):
try:
if self._mbusclient.is_open():
self.set_data(self._read_data(self.tag['address'],1)[0])
except Exception as e:
print("Error during data",self.tag['name'] ,"reading.")
def write_card(self):
try:
if self._mbusclient.is_open():
self._write_data_fcn(self.tag['address'],self._get_data())
except Exception as e:
print("Error during data" ,self.tag['name'] ,"writting.")
class CardHoldingRegister(DataCard):
def __init__(self,tag,mbusClient,**kwargs):
super().__init__(tag,mbusClient,**kwargs)
self._read_data=self._mbusclient.read_holding_registers
self._write_data_fcn= self._mbusclient.write_single_register
def set_data(self, data):
self.ids.textfield.text= str(data)
def get_data(self):
return int(self.ids.textfield.text)
class CardInputRegister(DataCard):
def __init__(self,tag,mbusClient,**kwargs):
super().__init__(tag,mbusClient,**kwargs)
self._read_data= self._mbusclient.read_input_registers
def set_data(self, data):
self.ids.label.text= str(data)
class CardCoil(DataCard):
def __init__(self,tag,mbusClient,**kwargs):
super().__init__(tag,mbusClient,**kwargs)
self._read_data= self._mbusclient.read_coils
self._write_data_fcn= self._mbusclient.write_single_coil
def set_data(self, data):
self.ids.switch.active= data #True / False
def get_data(self):
return self.ids.switch.active #RETURN = ajuste
最后,我们有了 kivy .kv 模块,它与 datacrd 模块对话(例如,当你想在 MDcard 盒子上显示服务器和客户端之间的数据交换时)并与主要模块(例如连接和 MD)对话
#:kivy 1.11.1
<MyWidget>:
MDBoxLayout:
orientation:'vertical'
MDTopAppBar:
title: "Informatica industrial"
MDBottomNavigation:
panel_color: app.theme_cls.accent_color
text_color_normal:0.4,0.4,0.4,1
text_color_active:0.8,0.8,0.8,1
MDBottomNavigationItem:
name:"config"
text:"CONFIGURATION"
icon: "cog"
MDBoxLayout:
orientation: 'vertical'
padding: "20p"
spacing: "50dp"
Image:
source:"imgs/modbus.png"
pos_hint: {"center_x":0.5,"center_y":0.5}
size_hint: {1,0.2}
MDTextField:
id:hostname
text:"127.0.0.1"
hint_text: "IP Address"
size_hint: {0.3, None}
height: "60dp" #tentatativa e erro
pos_hint: {"center_x":0.5,"center_y":0.5}
MDTextField:
id:port
text:"502"
hint_text: "Port"
size_hint: {0.3, None}
height: "60dp"
pos_hint: {"center_x":0.5,"center_y":0.4}
MDRoundFlatIconButton:
id: bt_con
text:"CONNECT"
icon:'connection'
pos_hint:{"center_x":0.5,"center_y":0.3}
on_release: root.connect()
MDBottomNavigationItem:
name:"data"
text:"DADOS"
icon:"chart-donut"
ScrollView:
size_hint: (1,None)
size: 800,600 #define o tamanho como o da janela 800x600
bar_pos_y: 'left'
bard_width: 20
effect_cls: 'ScrollEffect'
MDStackLayout:
id: modbus_data
size_hint: (1, None)
padding: 0.05*600,"150dp"
spacing: (800/5 - 2*0.05*800)/3
adaptive_height: True
<DataCard>:
orientation: 'vertical'
padding: '10dp'
size_hint:None,None
size: 600/5 , "90dp"
pos_hint:{"center_x": 0.5, "center_y":0.5}
MDLabel:
text: root.title
size_hint_y: None
height:self.texture_size[1]
pos_hint: {'top':1}
MDSeparator:
height: "1dp"
<CardHoldingRegister>:
MDTextField:
id:textfield
helper_text: "Pressione Enter para enviar os dados"
helper_text_mode:'persistent'
multiline: False
on_text_validate: root.write_card
<CardInputRegister>:
MDLabel:
id:label
<CardCoil>:
MDSwitch:
id: switch
如果问题太长,我很抱歉,但仅此而已。如果您愿意,我可以将我所基于的 Youtube 播放列表留在此处 (https://www.youtube.com/watch?v=DqO-KJXv6UE&list=PLDBnf2G73PkBqYVoxUoGQe7htYYE4fIsX&index=20)。老师还分享了服务器模块,可以在上面的 Youtube 链接上下载(在视频说明上)。我很感激任何类型的提示,我预先感谢你们。
根据 文档
ModbusClient.is_open
是一个布尔值(不是函数)。这意味着尝试将其用作函数if self._mbusclient.is_open()
将触发您所看到的异常。
要修复,请将
if self._mbusclient.is_open()
替换为 if self._mbusclient.is_open
(即删除 ()
。我不保证这是唯一的问题:-)...
无需 PC 即可使用该功能,可以将 apk 与 google colabs 进行转换。
"
from kivymd.app import MDApp
from kivymd.uix.label import MDLabel
from pymodbus.client import ModbusTcpClient as ModbusClient
from pymodbus.exceptions import ConnectionException
from kivy.clock import Clock
class MainApp(MDApp):
def build(self):
self.label = MDLabel(halign="center")
try:
self.client = ModbusClient('192.168.18.107', port=502)
connection = self.client.connect()
if not connection:
raise ConnectionException("Falha ao conectar com o dispositivo Modbus")
# Agenda a leitura da variável para acontecer a cada 1 segundo
Clock.schedule_interval(self.read_modbus_variable, 1)
except ConnectionException as e:
self.label.text = str(e)
except Exception as e:
self.label.text = f"Erro ao inicializar o Modbus: {e}"
return self.label
def read_modbus_variable(self, dt):
try:
# Ler a variável desejada
response = self.client.read_holding_registers(1, 1)
if not response.isError():
valor = response.registers[0]
texto = f"Valor lido: {valor}"
else:
texto = "Erro na leitura do registro"
self.label.text = texto
except ConnectionException as e:
self.label.text = "Falha na conexão com o dispositivo Modbus"
except Exception as e:
self.label.text = f"Falha ao ler o Modbus: {e}"
def on_stop(self):
# Desconectar quando o aplicativo for fechado
try:
if self.client:
self.client.close()
except Exception as e:
print(f"Erro ao fechar a conexão Modbus: {e}")
MainApp().run()
"