Maix Dock使用MaixUI(二)

说明

主要围绕这main.py core.py的解读和介绍,探索MaixUI的具体的实现形式

主函数说明

主函数包含了launcher类,类里设置load free draw event四个方法

load的方法是在_class_.ctrl加载一个agent的对象(软定时器,具体介绍在下边),在加载的agent对象中加入新的事件event(20,_class_.draw),也就是将launcher类的draw方法也加载进来。

其他两个类,free做释放,event是执行agent对象里的cycle方法。

import time, gc, math, sys

try:
  from core import agent, system
  from dialog import draw_dialog_alpha
  from ui_canvas import ui, print_mem_free
  from ui_container import container
  from wdt import protect
  from creater import get_time_curve
except ImportError as e:
  sys.print_exception(e)
  from lib.core import agent, system
  from lib.dialog import draw_dialog_alpha
  from ui.ui_canvas import ui, print_mem_free
  from ui.ui_container import container
  from driver.wdt import protect
  from lib.creater import get_time_curve

class launcher:

  def load():
    __class__.ctrl = agent()
    __class__.ctrl.event(20, __class__.draw)

  def free():
    __class__.ctrl = None

  @ui.warp_template(ui.blank_draw)
  @ui.warp_template(ui.grey_draw)
  @ui.warp_template(ui.bg_in_draw)
  @ui.warp_template(ui.anime_in_draw)
  @ui.warp_template(ui.help_in_draw)
  #@ui.warp_template(taskbar.time_draw)
  #@ui.warp_template(taskbar.mem_draw)
  #@catch # need sipeed_button
  def draw():
    height = 100 + int(get_time_curve(3, 250) * 60)
    pos = draw_dialog_alpha(ui.canvas, 20, height, 200, 20, 10, color=(255, 0, 0), alpha=200)
    ui.canvas.draw_string(pos[0] + 10, pos[1] + 10, "Welcome to MaixUI", scale=2, color=(0,0,0))
    ui.display()

  def event():
    __class__.ctrl.cycle()

if __name__ == "__main__":
  container.reload(launcher)
  while True:
    while True:
      last = time.ticks_ms() - 1
      while True:
        try:
          #time.sleep(0.1)
          #print(1000 // (time.ticks_ms() - last), 'fps')
          last = time.ticks_ms()

          gc.collect()
          container.forever()
          system.parallel_cycle()

          protect.keep()
          #gc.collect()
          #print_mem_free()
        except KeyboardInterrupt:
          protect.stop()
          raise KeyboardInterrupt
        #except Exception as e:
          #gc.collect()
          #print(e)
        finally:
          try:
            ui.display()
          except:
            pass

在main函数中,container.reload(launcher) ,很简单就是把launcher放进容器内。

程序里用两个while true,这里面猜测是因为预防其死机,一般情况不符合编程逻辑。本人测试了一下,去除一个while true 程序依然没有问题,可以执行。

在第三个while true中,gc.clooect()先清理没有被使用的内存。(python的垃圾回收机制应该和java差不多,每个被应用的方法和变量都会有关联符号,回收机制开启时,没有与在运行的函数有关联的,自动释放掉,以保证狭小的内存有足够空间)

container.forever()  (前期猜测是保存launcher数据,后面再详细分析)

system.parallel_cycle   parallel字面意思就是分布式并行的意思,这面system是在agent内的,伴随着agent的创建,实现全局型实例软定时器对象。这里pallel_cycle更是表现为循环型展示。

protect.keep()    protect字面意思就是保护的意思,学过单片机的同学都知道看门狗WDT,这面也是使用的看门狗,主要目的是防止死机。看门狗的运行原理就是,嵌入式设备内有一块硬件单元,定时检测一下程序运行情况,如果死机了,就辅助硬件设备重启,其实更多的是监控pc指针的运行情况,pc指针不继续进行下去,就表示死机。

:因为硬件核数有限,频率低,这里尽量避免使用线程,而更多的使用agent软定时器,实现分时使用。难度很大,但是对于嵌入式设备既想实现控制,又想实现UI展示,分时使用是为数不多的选择之一。

core函数解读(包含agent的)

agent类里包含了msg 和 arg的基本变量。除去简单的get_ms方法,着重看一下event / call / cycle / parallel_cycle

event有四个入参self,cycle,func,args=None,然后将这几个参数打包起来,添加到msg内去。我们在main.py内也看到,__class__.ctrl.event(20, __class__.draw) 可以看出入参了两个数据,一个是20ms,一个launch.draw方法.  第一个入参和最后一个入参,该方法在创建的时候默认输入进去。

  • self 是实例化的agent地址
  • event入参的cycle 推测是延时时间,也就是该方法占用的程序时间(单位ms)
  • func 就是方法
class agent:

  def __init__(self):
    self.msg = []
    self.arg = {}

  def get_ms(self):
    import time
    # return time.time() * 1000
    return time.ticks_ms()

  def event(self, cycle, func, args=None):
    # arrived, cycle, function
    tmp = (self.get_ms() + cycle, cycle, func, args)
    #print('self.event', tmp)
    self.msg.append(tmp)

  def remove(self, func):
    #print(self.remove)
    for pos in range(len(self.msg)): # maybe use map
      #print(self.msg[pos][2], func)
      if self.msg[pos][2] == func:
          self.msg.remove(self.msg[pos])
          break
    #print(self.msg)

  def call(self, obj, pos=0):
    print(self.call, pos, obj)
    self.msg.pop(pos)
    self.event(obj[1], obj[2], obj[3])
    obj[2](obj[3]) if obj[3] else obj[2]() # exec function

  def cycle(self):
    if (len(self.msg)):
      obj = self.msg[0]
      if (self.get_ms() >= obj[0]):
        self.call(obj, 0)

  def parallel_cycle(self):
    for pos in range(len(self.msg)): # maybe use map
      obj = self.msg[pos]
      if (self.get_ms() >= obj[0]):
        self.call(obj, pos)
        break

  def unit_test(self):

    class tmp:
        def test_0(self):
          print('test_0')

        def test_1():
          print('test_1')

        def test_2(self):
          print('test_2')
          self.remove(tmp.test_1)
          self.event(1000, tmp.test_1)
          self.remove(tmp.test_2)

    import time
    self.event(200, tmp.test_0, self)
    self.event(10, tmp.test_1)
    self.event(2000, tmp.test_2, self)
    while True:
      self.parallel_cycle()
      time.sleep(0.1)

system = agent()

if __name__ == "__main__":
  #agent().unit_test()
  system.unit_test()

system=agent 也就是只要创建agent对象的时候,就会创建一个全局的agent软分时器。

cycle

  def cycle(self):
    if (len(self.msg)):
      obj = self.msg[0]
      if (self.get_ms() >= obj[0]):
        self.call(obj, 0)

这个程序,最主要的应该是cycle方法的使用了,cycle方法的逻辑是先判断msg队列是否有数据,如果有数据,我们将第一个对象obj=self.msg[0]拿出来,取出的对象其实就是event()压进去的tmp,是一个打包型的数据, tmp = (self.get_ms() + cycle, cycle, func, args) ,再拿出obj[0]数据,也就是self.get_ms()+cycle这个具体的数据和当前的时间进行对比,如果对比成立(现在的时间肯定晚于之前的时间),然后调用call方法,接下来我们分析一下call这个方法

  def call(self, obj, pos=0):
    print(self.call, pos, obj)
    self.msg.pop(pos)
    self.event(obj[1], obj[2], obj[3])
    obj[2](obj[3]) if obj[3] else obj[2]() # exec function

我这里打印了一下print(self.call,pose,obj)

这里面打印的第一条是call这个方法的地址,后面紧跟着是实例化的agent对象,0是pos数据,后面跟的都是obj内的参数。

可以参考https://blog.csdn.net/qq_15158911/article/details/86413390

防止头脑发散,我们接着看,self.msg.pop(pos) 移除指定位置的数据,这里面pos是0,也就是移除队列里第一个数,然后将obj[1],obj[2],obj[3](数据是20ms,draw函数,参数)

 obj[2](obj[3]) if obj[3] else obj[2]()  推测大概意思是将参数放入变量里,如果参数没有,就直接使用无参的变量。

parallel_cycle

  def parallel_cycle(self):
    for pos in range(len(self.msg)): # maybe use map
      obj = self.msg[pos]
      if (self.get_ms() >= obj[0]):
        self.call(obj, pos)
        break

和cycle的方法是一样的,只是cycle始终默认pos=0,而parallel·是所有的都需要遍历一遍。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值