二级系统开发学习记录(二)RabbitMQ与Python与MongoDb,多线程,任务队列、读取plc、写入db
Posted on 2025-04-15 16:12 魍酱 阅读(25) 评论(0) 收藏 举报书接上文。
这次是通过消费者将信息推入任务队列,实现线程之间的任务队列共享和处理。
这里需要使用到python的threading类来实现多线程,使用pymongo来操作数据库,使用snap7来操作plc相关的。我使用的是西门子的博图和plcsimadvance来进行plc仿真。
先创建一个类,继承threading.Thread。
class PLCController(threading.Thread):
def __init__(self,queue,ip_address, rack=0, slot=1,):#机架号和槽位基本不会变就写死了,有需要也可以再改动
super().__init__()
self.plc = snap7.client.Client() #创建一个plc客户端,后面用得上
self.ip_address = ip_address #想要根据不同的ip来创建客户端,很久以后或许用的上
self.rack = rack
self.slot = slot
self.connected = False #与plc的链接状态,默认是f
self.data = {} #最后要存到数据库里面的数据
self.reporting = False #是否储存
self.running = False #线程运行标识
self.queue = queue #任务队列,main里面会把消费者创建的任务队列传到这里
self.heatid = "" #以后的其他服务会传这个值过来,现在仙女作为测试
self.db = pymongo.MongoClient("mongodb://localhost:27017/").VOD #db中的文件夹
先是获取任务的方法
def _process_tasks(self):
try:
while True:
task = self.queue.get_nowait() #用getnowait,没有任务的时候也不会阻塞而是报错
if isinstance(task, dict):
for task_type, param in task.items():
self._handle_single_task(task_type, param)
else:
self._handle_single_task(task, None)
self.queue.task_done() #遍历然后挨个处理任务,把任务名和键值写入
except Empty:
pass
except Exception as e:
logging.error(f"任务处理失败: {e}")
然后是处理任务的方法
def _handle_single_task(self, task_type, param):
try:
match task_type:
case "reporting":
if isinstance(param, bool):
self.reporting = param
logging.info(f"报告状态已设置为: {param}")
else:
logging.error(f"参数类型错误:'reporting'需要布尔值,收到 {type(param)}") #将记录的状态设置为参数
case "heatid":
self.heatid = str(param)
logging.info(f"生产批次ID已更新: {param}") #更新heatid
case "link":
self.connect()#链接plc
case "disconnect":
self.disconnect()#断链plc
case "stop":
self.stop()#停止服务
case _:
logging.warning(f"未知任务类型: {task_type}")
except Exception as e:
logging.error(f"任务处理失败:类型={task_type}, 参数={param}, 错误={e}")
然后是建立和断开plc链接的方法
def connect(self):
try:
if not self.connected:
self.plc.connect(self.ip_address, self.rack, self.slot)
self.connected = self.plc.get_connected()
logging.info(f"成功连接到 {self.ip_address}")
except Exception as e:
logging.error(f"连接失败: {e}")
def disconnect(self):
try:
if self.connected:
self.plc.disconnect()
self.connected = False
logging.info(f"已断开 {self.ip_address} 连接")
except Exception as e:
logging.error(f"断开连接失败: {e}")
理论上来说断开链接还需要把客户端给删除,但是要长期运行应该不需要,后面测试再看吧。
然后是读取plc数据的方法
def main_get(self):
if not self.connected:
return
try:
maindb = self.plc.read_area(snap7.client.Area.DB, 200, 0, 34) # 读取 DB200 的数据,200是db号,0和34是db里面的0-34字节,如果超过实际的字节数就会报错
# 解析数据并存储
self.data.update({
"looper": snap7.util.get_bool(maindb, 0, 0),
'co': snap7.util.get_real(maindb, 2),
'co2': snap7.util.get_real(maindb, 6),
'o2': snap7.util.get_real(maindb, 10),
'gas_flow': snap7.util.get_real(maindb, 14),
'tank_vac_value': snap7.util.get_real(maindb, 18),
'pump_vac_value': snap7.util.get_real(maindb, 22),
'high_vac_value': snap7.util.get_real(maindb, 26),
'some_value': snap7.util.get_real(maindb, 30),
'ip': self.ip_address,
'timestamp': datetime.datetime.now()
})
# 记录数据到数据库
if self.reporting:
self.save_to_db()
except Exception as e:
logging.error(f"PLC数据读取失败: {e}")
self.disconnect() # 尝试重新连接
self.connect()
然后是写入mongodb
def save_to_db(self):
try:
record = self.data.copy()
record["heatid"] = self.heatid
self.db.sensor_data.insert_one(record)
except Exception as e:
logging.error(f"数据库写入失败: {e}")
最后是主循环
def run(self):
self.running = True
self.connect()
print("run")
try:
while self.running:
self.main_get()
self._process_tasks()
time.sleep(3)
finally:
self.disconnect()
轮询定为3秒,太短了数据太多了。
负责调用的main函数像这样
customer = customer.Customer()
plccontroller = read.PLCController(customer.queue,"192.168.0.1",rack=0,slot=1)
if __name__ == "__main__":
customer.start()
plccontroller.start()
plccontroller.connect()
很奇怪的是每次启动都会报错
b' TCP : Socket operation on non socket'
ERROR:root:连接失败: b' TCP : Socket operation on non socket'
ERROR:snap7.common:b' TCP : Socket operation on non socket'
ERROR:root:连接失败: b' TCP : Socket operation on non socket'
后面再想想怎么处理,测试是不太影响使用的。
使用测试的json
{
'link':True,
'reporting':True,
'heatid': "asd"
}
最后写入到mongodb的数据
{
"_id": {
"$oid": "67fe0ddaa629e40120af4453"
},
"looper": false,
"co": 0,
"co2": 0,
"o2": 0,
"gas_flow": 0,
"tank_vac_value": 0,
"pump_vac_value": 0,
"high_vac_value": 0,
"some_value": 0,
"ip": "192.168.0.1",
"timestamp": {
"$date": "2025-04-15T15:42:18.991Z"
},
"heatid": "asd"
}
浙公网安备 33010602011771号