工业仿真(simulation)-- 装配站,拆卸站(4)

80 阅读3分钟

本章内容主要讲解工业仿真软件里面的装配站实体和拆卸站实体,首先看两者的定义

简介

装配站是什么

装配站是生产线上的一个特定工位,在这个工位上,操作工人和/或自动化设备将多个零件、部件或子系统按照设计要求组合、连接、安装在一起,最终形成一个功能更完整的产品或半成品。

拆卸站是什么

拆卸站是一个专门用于将成品、半成品或退回产品进行系统性分解,拆解成其组成部分的工位。

两者的区别

设计与开发

首先我们来看装配站

装配站设计思路

装配站是将多个产品结合成一个产品,假如是装配站前面只有一个设备加工站,那么装配站可以指定设备A必须传进来指定的产品数量,才能继续加工

如果装配站前面有多个设备,如下图

那么用户在使用装配站时,就必须指定设备ABC三台设备需要加工的,那么我们就需要在装配站里面维护一个设备加工数量映射表,类似于下面

设备A:A(n)

设备B:B(n)

设备C:C(n)

并且还需要再维护一个当前已传入产品的设备映射表

设备A:A(m)

设备B:B(m)

设备C:C(m)

到这一步,我们就可以梳理一个大致的加工逻辑

  1. 当有产品传入时,需要先判断是否需要这个产品
  2. 如果 m < n ,就表示可以接受该产品,负责就拒绝该产品
  3. 当接收到新产品后,在判断现有的条件是否满足加工条件
  4. 加工完毕后,进行派发产品,清空所有 m

接下来我们看代码实现

装配站代码实现

1. 属性值

  processTime: randomTime        // 加工时间

  requiredInputs: RequiredInputMap          // 每个来源站点需要的产品数量

  bufferMap: SourceProductMap = {}        // 当前装配站上已有产品的设备映射表

  currentNewProduct: Product | null = null        // 新的产品

  productWidth: number        // 新产品宽度

  productHeight: number        // 新产品高度

2. 接收产品方法


  //判断是否可以接收上游传递的产品
  public canReceiveProduct(name: string): boolean {
    if (this.status !== 'idle') {
      return false
    }
    if (this.bufferMap[name] === undefined) {
      return true
    }
    if (this.bufferMap[name].length < this.requiredInputs[name]) {
      return true
    }
    return false
  }

  //接受就绪产品
  receiveReadyProduct(productId: string): void {
    if (this.status === 'idle') {
      this.readyProduct = productId
      const product = getReadyProduct(productId)
      if (!product) {
        console.log(`[${currentTime}] ❌ ${productId} 没有发现`)
        return
      }
      console.log(`[${currentTime}] ${product.id} 已到达 --> ${this.name}`)
      this.onProductReceived(product)
    } else {
      console.log(`[${currentTime}] ${this.name} 不在空闲状态,无法接收产品 ${productId}`)
    }
  }

3. 合成产品

protected onProductReceived(product: Product): void {
    const source = product.from || 'unknown'
    product.setFrom(this.id)
    console.log(`[${currentTime}] ${product.id} 到达装配站 ${this.name},来自 ${source}`)
    messageTransfer('product', 'move', { targetId: this.id, productId: product.id })

    // 将产品加入对应来源的缓存
    if (!this.bufferMap[source]) {
      this.bufferMap[source] = []
    }
    this.bufferMap[source].push(product)
    // 检查是否满足所有来源的数量要求
    this.tryAssemble()
  }

  //判断是否可以开始装配
  private canAssemble(): boolean {
    for (const [source, requiredCount] of Object.entries(this.requiredInputs)) {
      if (
        !this.bufferMap[source] ||
        this.bufferMap[source].length === 0 ||
        this.bufferMap[source].length < requiredCount
      ) {
        return false
      }
    }
    return true
  }

  private tryAssemble(): void {
    if (!this.canAssemble()) return
    this.setStatus('processing')

    const parts: Product[] = []
    for (const [source, count] of Object.entries(this.requiredInputs)) {
      const group = this.bufferMap[source].splice(0, count)
      parts.push(...group)
    }

    // 生成新的产品
    const newProduct = new Product(generateUUID(), this.productWidth, this.productHeight)
    this.currentNewProduct = newProduct
    messageTransfer('product', 'generate', { targetId: this.id, productId: newProduct.id })
    messageTransfer('product', 'startProcessing', { targetId: this.id, productId: newProduct.id })
    this.currentNewProduct = newProduct

    // 消除旧的产品
    for (const p of parts) {
      messageTransfer('product', 'recycle', {
        targetId: this.id,
        productId: p.id
      })
    }

    messageTransfer('product', 'startProcessing', {
      targetId: this.id,
      productId: newProduct.id
    })

    const time = typeof this.processTime === 'function' ? this.processTime() : this.processTime
    console.log(`[${currentTime}] 🛠️ ${this.name} 开始装配 ${newProduct.id}`)

    schedule(time, () => this.finishAssemble(newProduct), `${this.id} 完成装配 ${newProduct.id}`)
  }

  private finishAssemble(newProduct: Product): void {
    messageTransfer('product', 'finishProcessing', { targetId: this.id, productId: newProduct.id })
    //当前产品添加到就绪产品队列中
    addReadyProduct(newProduct)
    //产品加工完毕,尝试派发产品
    this.setStatus('block')
  }

新产品在加工完毕后,会将状态改为block

此时 setStatus () 方法会触发设备调度器调度功能,自动寻找下一站空闲的设备

4. 派发产品

派发产品功能和加工站实体类一致,都是寻找下游空闲设备,然后将新产品派发给它

  tryDispatchCurrentProduct(): void {
    if (!this.currentNewProduct) return
    const productId = this.currentNewProduct.id
    for (const next of this.nextStations) {
      if (next.canReceiveProduct(this.id, this.currentNewProduct)) {
        this.currentNewProduct = null
        next.receiveReadyProduct(productId)
        //清空缓冲区
        for (const key of Object.keys(this.bufferMap)) {
          this.bufferMap[key] = []
        }
        this.setStatus('idle')
        break
      }
    }
  }

装配站仿真逻辑介绍到这,接下来介绍拆卸站

拆卸站设计思路

拆卸站是将一个产品拆成多个产品,如下图所示,拆卸站后面有三台设备,如果拆卸站的拆卸数量为4,则在拆卸时提示错误,【拆卸的数量不能多余下游设备数量】

若拆卸数量小于下游设备数量,例如拆卸数量为2,则下游三台设备将会有一台设备不会分的产品

并且只有在设备ABC 三台设备都处于空闲状态时,拆卸站才会将已拆好的产品派发给三台设备

拆卸站代码实现

1. 属性值

  processTime: randomTime        // 拆卸时间

  dismantlingQuantity: number        // 拆卸数量

  private splitProduts: Product[] = []        // 拆卸后新产品的数组

  productWidth: number        // 新产品宽度

  productHeight: number        // 新产品高度

2. 接收产品

  public canReceiveProduct(): boolean {
    return this.status === 'idle'
  }

  protected onProductReceived(product: Product): void {
    messageTransfer('product', 'move', { targetId: this.id, productId: product.id })
    messageTransfer('product', 'startProcessing', { targetId: this.id, productId: product.id })
    console.log(`[${currentTime}] ${product.id} 到达拆解站 --> ${this.name}`)
    this.tryDisassamble(product)
  }

  private tryDisassamble(product: Product): void {
    this.setStatus('processing')
    const time = typeof this.processTime === 'function' ? this.processTime() : this.processTime
    schedule(time, () => this.disassemble(product), `${this.id} 拆解 ${product.id}`)
  }

3. 拆解产品

  private disassemble(product: Product): void {
    console.log(`[${currentTime}] ${this.name} 拆出 ${this.dismantlingQuantity} 个部件`)
    messageTransfer('product', 'recycle', {
      targetId: this.id,
      productId: product.id
    })

    const parts: Product[] = []
    for (let i = 0; i < this.dismantlingQuantity; i++) {
      const newPart = new Product(generateUUID(), this.productWidth, this.productHeight)
      newPart.setFrom(this.id)
      parts.push(newPart)
      messageTransfer('product', 'generate', { targetId: this.id, productId: newPart.id })
    }

    this.splitProduts = parts
    this.setStatus('block')
  }

4. 派发产品

  public tryDispatchCurrentProduct(): void {
    if (this.nextStations.length < this.dismantlingQuantity) {
      console.warn(`[${currentTime}] ⚠️ ${this.id} 的下游站点数量不足`)
      return
    }

    let mark = false
    for (const next of this.nextStations) {
      if (!next.canReceiveProduct(this.id, this.splitProduts[0])) {
        mark = true
        break
      }
    }

    if (mark) {
      console.log(`[${currentTime}] ⛔ ${this.name} 下游不空闲,等待中...`)
      return
    } else {
      for (let i = 0; i < this.splitProduts.length; i++) {
        const product = this.splitProduts[i]
        const station = this.nextStations[i]
        addReadyProduct(product)
        station.receiveReadyProduct(product.id)
      }

      this.setStatus('idle')
    }
  }

装配站和拆卸站的仿真逻辑就讲到这里,谢谢大家