I created an app, based on a teacher's videoclass program. It turned out I had some issues, returning me the following exceptions and Id badly apreciate your attention:
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.
I know it's a theoric script, but I'd appreciate If u guys could run in your computers, because I hand-copied the teacher's scripts but It simply doesnt work althought it has no errors. As long as I post here the codes, Ill keep writting here its overviews and the files will be 3 (the kivyMD .kv, the main.py and the datacards). The app appearence is like that: The issues trigger exactly on the page below (data acquisition) because theonnection step is succesful.
Starting with the main, this code instantiates the MDApp and sequentially the Screen. It has a button, as seen on the picture1, for connecting. The connecting method himself instantiates the clock scheduler for eventual updates on the holding card and on the coil card.
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()
The second file and next step is the datacard module. Its goal is to define 2 classes the MDcard one, and the 3 different MDcard children. Actually the 3 of them are repreenting the methodology differences between Mbus functions Holding, Input or Coil. Their goil is to interface with the server:
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
At last, we have the kivy .kv module, it diallogues with the datacrd module (e.g. when u want to show on the MDcard boxes the data exchanges between server and client) and with the main one (e.g connection and 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
Im sorry if the question went too long, but that's it. If u prefer, I leave here the Youtube playlist which I'm basing on (https://www.youtube.com/watch?v=DqO-KJXv6UE&list=PLDBnf2G73PkBqYVoxUoGQe7htYYE4fIsX&index=20). The teacher also shared the server module and It can be downloaded on the Youtube link above (on video descriptions). I appreciate any kind of tips and I thank u all beforehand.
As per the docs ModbusClient.is_open
is a boolean (not a function). This means that attempting to use it as a function if self._mbusclient.is_open()
will trigger the exception you are seeing.
To fix replace if self._mbusclient.is_open()
with if self._mbusclient.is_open
(i.e. remove the ()
. I'm not guaranteeing that is the only issue :-)...