728x90
#
# ver : 0.4
from tkinter import *
from PIL import Image, ImageTk
from datetime import *
import time
import random
import pymysql
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import numpy as np
from matplotlib.figure import Figure
from USB_RELAY_UART import USB_RELAY
class Dashboard:
def __init__(self, window):
self.start_time = time.time()
self.window = window
self.window.geometry('1366x768')
self.window.resizable(0, 0)
self.window.state('zoomed')
self.window.config(background='#EFF5F6')
# left Frame
self.SV_TEMP_BTN_STATUS = -1
self.AUTO_TEMP_BTN_STATUS = -1
self.FANIN_BTN_STATUS = -1
self.FANOUT_BTN_STATUS = -1
self.HEAT_BTN_STATUS = -1
self.SV_TEMP_VAL = 0
self.SV_HUMI_VAL = 0
self.PV_TEMP_VAL = random.randint(20, 30)
self.PV_HUMI_VAL = random.randint(20, 70)
self.PV_TEMP_DATA = []
self.SV_TEMP_DATA = []
# right Frame
self.AUTO_GAS_BTN_STATUS = -1
self.GAS_VV_BTN_STATUS = -1
self.SV_GAS_1_BTN_STATUS = -1
self.SV_GAS_2_BTN_STATUS = -1
self.SV_GAS_3_BTN_STATUS = -1
self.SV_GAS_1_BTN = 'SVG1'
self.SV_GAS_2_BTN = 'SVG2'
self.SV_GAS_3_BTN = 'SVG3'
self.SV_GAS_1_VAL = 0
self.SV_GAS_2_VAL = 0
self.SV_GAS_3_VAL = 0
self.PV_GAS_1_VAL = random.randint(10, 500)
self.PV_GAS_2_VAL = random.randint(10, 500)
self.PV_GAS_3_VAL = random.randint(10, 500)
self.SV_GAS_1_DATA = []
self.SV_GAS_2_DATA = []
self.SV_GAS_3_DATA = []
self.PV_GAS_1_DATA = []
self.PV_GAS_2_DATA = []
self.PV_GAS_3_DATA = []
# time
self.T = []
# Frame Set Up
self.topFrame = Frame(self.window, bg='#000000')
self.topFrame.place(x=0, y=0, width=1366, height=200)
# Figure_1 : 온도
# self.fig1 = Figure(facecolor='#000000')
self.fig1 = Figure()
self.fig1 = Figure(facecolor='#000000')
self.ax1 = self.fig1.add_subplot(1, 1, 1)
self.canvas1 = FigureCanvasTkAgg(self.fig1, self.topFrame)
self.canvas1._tkcanvas.place(x=60, y=10, width=500, height=180)
self.date_time = Label(self.topFrame, bg='#CCFFCC')
self.date_time.place(x=600, y=100)
# Figure_2 : 가스
# self.fig2 = Figure(facecolor='#000000')
self.fig2 = Figure()
self.fig2 = Figure(facecolor='#000000')
self.ax2 = self.fig2.add_subplot(1, 1, 1)
self.canvas2 = FigureCanvasTkAgg(self.fig2, self.topFrame)
self.canvas2._tkcanvas.place(x=780, y=10, width=500, height=180)
# leftFrame Layout : 온도, 습도 제어
self.leftFrame = Frame(self.window, bg='#F0DADA')
self.leftFrame.place(x=0, y=200, width=683, height=568)
self.sv_temp_button = Button(self.leftFrame, text='SV 온도 (℃)', bg='#D0DEF0', font=('', 12, 'bold'), bd=6,
cursor='hand2', activebackground='#C4C23E',
command=lambda: self.sv_temp_btn(self.SV_TEMP_BTN_STATUS))
self.sv_temp_button.place(x=50, y=20, width=250, height=50)
self.sv_humi_button = Button(self.leftFrame, text='SV 습도 (RH %)', bg='#D0DEF0', font=('', 12, 'bold'), bd=6,
state=DISABLED)
self.sv_humi_button.place(x=370, y=20, width=250, height=50)
self.sv_temp_entry = Entry(self.leftFrame, bg='#FFFFFF', font=(
'', 18, 'bold'), justify='center', fg='blue')
self.sv_temp_entry.insert(0, self.SV_TEMP_VAL)
self.sv_temp_entry.place(x=50, y=80, width=250, height=50)
self.sv_humi_entry = Entry(self.leftFrame, bg='#FFFFFF', font=(
'', 18, 'bold'), justify='center', fg='blue')
self.sv_humi_entry.insert(0, self.SV_HUMI_VAL)
self.sv_humi_entry.place(x=370, y=80, width=250, height=50)
# 온도 자동 운전 버튼
self.auto_temp_button = Button(self.leftFrame, text='운 전 (자 동)', bg='#DEE2E9', font=('', 18, 'bold'), fg='red', bd=6,
command=lambda: self.auto_temp_btn(
self.AUTO_TEMP_BTN_STATUS),
cursor='hand2', activebackground='#C4C23E')
self.auto_temp_button.place(x=50, y=140, width=570, height=50)
# 온도 PV 표시 label
self.pv_temp_label = Label(
self.leftFrame, text='PV 온도 (℃)', bg='#CCFFCC', font=('', 14, 'bold'), fg='blue')
self.pv_temp_label.place(x=50, y=210, width=250, height=50)
self.pv_humi_label = Label(
self.leftFrame, text='PV 습도 (RH %)', bg='#CCFFCC', font=('', 14, 'bold'), fg='blue')
self.pv_humi_label.place(x=370, y=210, width=250, height=50)
# 온도 PV 측정값 표시
self.pv_temp_value = Label(
self.leftFrame, text=self.PV_TEMP_VAL, bg='#FFFFFF', font=('', 14, 'bold'))
self.pv_temp_value.place(x=50, y=270, width=250, height=50)
self.pv_humi_value = Label(
self.leftFrame, text=self.PV_HUMI_VAL, bg='#FFFFFF', font=('', 14, 'bold'))
self.pv_humi_value.place(x=370, y=270, width=250, height=50)
# 급기팬 제어 상태
self.fanin_button = Button(self.leftFrame, text='급기팬 Off', bg='#FFFFFF', font=('', 14, 'bold'), bd=6,
command=lambda: self.fanin_btn(self.FANIN_BTN_STATUS), state=DISABLED)
# state=DISABLED)
self.fanin_button.place(x=50, y=350, width=250, height=50)
# 배기팬 제어 상태
self.fanout_button = Button(self.leftFrame, text='배기팬 Off', bg='#FFFFFF', font=('', 14, 'bold'), bd=6,
command=lambda: self.fanout_btn(self.FANOUT_BTN_STATUS), state=DISABLED)
# state=DISABLED)
self.fanout_button.place(x=50, y=410, width=250, height=50)
# 펠티어 히팅 상태
self.heat_button = Button(self.leftFrame, text='HEAT Off', bg='#FFFFFF', font=('', 14, 'bold'), bd=6,
command=lambda: self.heat_btn(self.HEAT_BTN_STATUS), state=DISABLED)
# state=DISABLED)
self.heat_button.place(x=370, y=350, width=250, height=50)
# rightFrame Layout : 가스 제어
self.rightFrame = Frame(self.window, bg='#EEF7C9')
self.rightFrame.place(x=683, y=200, width=683, height=568)
# SV 설정값 셋팅 버튼 layout
self.sv_gas_1_button = Button(self.rightFrame, text='SV GAS #1 (ppm)', bg='#D0DEF0', font=('', 12, 'bold'), bd=6,
cursor='hand2', activebackground='#C4C23E',
command=lambda: self.sv_gas_btn(self.SV_GAS_1_BTN))
self.sv_gas_1_button.place(x=20, y=20, width=180, height=50)
self.sv_gas_2_button = Button(self.rightFrame, text='SV GAS #2 (ppm)', bg='#D0DEF0', font=('', 12, 'bold'), bd=6,
cursor='hand2', activebackground='#C4C23E',
command=lambda: self.sv_gas_btn(self.SV_GAS_2_BTN))
self.sv_gas_2_button.place(x=210, y=20, width=180, height=50)
self.sv_gas_3_button = Button(self.rightFrame, text='SV GAS #3 (ppm)', bg='#D0DEF0', font=('', 12, 'bold'), bd=6,
cursor='hand2', activebackground='#C4C23E',
command=lambda: self.sv_gas_btn(self.SV_GAS_3_BTN))
self.sv_gas_3_button.place(x=400, y=20, width=180, height=50)
# SV 설정값 입력 Entry layout
self.sv_gas_1_entry = Entry(self.rightFrame, bg='#FFFFFF', font=(
'', 18, 'bold'), justify='center', fg='blue')
self.sv_gas_1_entry.insert(0, self.SV_GAS_1_VAL)
self.sv_gas_1_entry.place(x=20, y=80, width=180, height=50)
self.sv_gas_2_entry = Entry(self.rightFrame, bg='#FFFFFF', font=(
'', 18, 'bold'), justify='center', fg='blue')
self.sv_gas_2_entry.insert(0, self.SV_GAS_2_VAL)
self.sv_gas_2_entry.place(x=210, y=80, width=180, height=50)
self.sv_gas_3_entry = Entry(self.rightFrame, bg='#FFFFFF', font=(
'', 18, 'bold'), justify='center', fg='blue')
self.sv_gas_3_entry.insert(0, self.SV_GAS_3_VAL)
self.sv_gas_3_entry.place(x=400, y=80, width=180, height=50)
# 가스 자동 운전 버튼
self.auto_gas_button = Button(self.rightFrame, text='운 전 (자 동)', bg='#DEE2E9', font=('', 18, 'bold'), fg='red', bd=6,
command=lambda: self.auto_gas_btn(
self.AUTO_GAS_BTN_STATUS),
cursor='hand2', activebackground='#C4C23E')
self.auto_gas_button.place(x=20, y=140, width=560, height=50)
# 가스 PV 표시 label
self.pv_gas_1_label = Label(
self.rightFrame, text='PV 가스 #1 (ppm)', bg='#CCFFCC', font=('', 14, 'bold'), fg='blue')
self.pv_gas_1_label.place(x=20, y=210, width=180, height=50)
self.pv_gas_2_label = Label(
self.rightFrame, text='PV 가스 #2 (ppm)', bg='#CCFFCC', font=('', 14, 'bold'), fg='blue')
self.pv_gas_2_label.place(x=210, y=210, width=180, height=50)
self.pv_gas_3_label = Label(
self.rightFrame, text='PV 가스 #3 (ppm)', bg='#CCFFCC', font=('', 14, 'bold'), fg='blue')
self.pv_gas_3_label.place(x=400, y=210, width=180, height=50)
# 가스 PV 측정값 표시 label
self.pv_gas_1_value = Label(
self.rightFrame, text=self.PV_GAS_1_VAL, bg='#FFFFFF', font=('', 14, 'bold'))
self.pv_gas_1_value.place(x=20, y=270, width=180, height=50)
self.pv_gas_2_value = Label(
self.rightFrame, text=self.PV_GAS_2_VAL, bg='#FFFFFF', font=('', 14, 'bold'))
self.pv_gas_2_value.place(x=210, y=270, width=180, height=50)
self.pv_gas_3_value = Label(
self.rightFrame, text=self.PV_GAS_3_VAL, bg='#FFFFFF', font=('', 14, 'bold'))
self.pv_gas_3_value.place(x=400, y=270, width=180, height=50)
# 가스 밸브 상태
self.gas_vv_button = Button(self.rightFrame, text='가스 밸브 CLOSE', bg='#FFFFFF', font=('', 14, 'bold'), bd=6,
command=lambda: self.gas_vv_btn(self.GAS_VV_BTN_STATUS), state=DISABLED)
self.gas_vv_button.place(x=20, y=350, width=560, height=50)
# 기타 함수들
self.show_time()
self.show_fig()
self.set_auto_temp()
self.set_auto_gas()
# 시계
def show_time(self, *_):
self.DATETIME = time.time()
self.TIME = time.strftime('%H:%M:%S')
self.DATE = time.strftime('%Y/%m/%d')
set_text = f'{self.DATE} \n {self.TIME}'
self.date_time.configure(text=set_text, font=(
'', 22, 'bold'), bd=0, bg='#000000', fg='#FFFFFF')
# DB 저장
#global conn, cursor
#sql = "INSERT INTO chamber_tb (DATETIME, SV_TEMP_BTN_STATUS, AUTO_TEMP_BTN_STATUS, FANIN_BTN_STATUS, FANOUT_BTN_STATUS, \
# HEAT_BTN_STATUS, SV_TEMP_VAL, SV_HUMI_VAL, PV_TEMP_VAL, PV_HUMI_VAL, \
# AUTO_GAS_BTN_STATUS, GAS_VV_BTN_STATUS, SV_GAS_1_BTN_STATUS, SV_GAS_2_BTN_STATUS, SV_GAS_3_BTN_STATUS, \
# SV_GAS_1_VAL, SV_GAS_2_VAL, SV_GAS_3_VAL, PV_GAS_1_VAL, PV_GAS_2_VAL, PV_GAS_3_VAL) \
# VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)"
#cursor.execute(sql, [self.DATETIME, self.SV_TEMP_BTN_STATUS, self.AUTO_TEMP_BTN_STATUS, self.FANIN_BTN_STATUS, self.FANOUT_BTN_STATUS,
# self.HEAT_BTN_STATUS, self.SV_TEMP_VAL, self.SV_HUMI_VAL, self.PV_TEMP_VAL, self.PV_HUMI_VAL,
# self.AUTO_GAS_BTN_STATUS, self.GAS_VV_BTN_STATUS, self.SV_GAS_1_BTN_STATUS, self.SV_GAS_2_BTN_STATUS, self.SV_GAS_3_BTN_STATUS,
# self.SV_GAS_1_VAL, self.SV_GAS_2_VAL, self.SV_GAS_3_VAL, self.PV_GAS_1_VAL, self.PV_GAS_2_VAL, self.PV_GAS_3_VAL])
#conn.commit()
self.date_time.after(500, self.show_time)
# figure graph
def show_fig(self, *_):
self.T.append(time.time()-self.start_time)
self.SV_TEMP_DATA.append(self.SV_TEMP_VAL)
self.PV_TEMP_DATA.append(self.PV_TEMP_VAL)
self.SV_GAS_1_DATA.append(self.SV_GAS_1_VAL)
self.SV_GAS_2_DATA.append(self.SV_GAS_2_VAL)
self.SV_GAS_3_DATA.append(self.SV_GAS_3_VAL)
self.PV_GAS_1_DATA.append(self.PV_GAS_1_VAL)
self.PV_GAS_2_DATA.append(self.PV_GAS_2_VAL)
self.PV_GAS_3_DATA.append(self.PV_GAS_3_VAL)
self.ax1.cla()
self.ax1.plot(self.T[-50:], self.SV_TEMP_DATA[-50:], 'w-.')
self.ax1.plot(self.T[-50:], self.PV_TEMP_DATA[-50:])
self.ax1.set_ylim(0, 100)
self.ax1.grid(color='r')
self.ax1.set_xlabel('temp')
#self.ax1.set_ylim(0, 100)
self.ax1.tick_params(colors='red')
self.ax1.legend(labels=['SV', 'PV'], loc='upper left')
self.ax1.set_title(str(len(self.PV_TEMP_DATA)), color='y')
self.ax1.patch.set_facecolor('#000000')
self.canvas1.draw()
self.ax2.cla()
self.ax2.plot(self.T[-50:], self.PV_GAS_1_DATA[-50:])
self.ax2.plot(self.T[-50:], self.PV_GAS_2_DATA[-50:])
self.ax2.plot(self.T[-50:], self.PV_GAS_3_DATA[-50:])
self.ax2.plot(self.T[-50:], self.SV_GAS_1_DATA[-50:], 'w-.')
self.ax2.plot(self.T[-50:], self.SV_GAS_2_DATA[-50:], 'y-.')
self.ax2.plot(self.T[-50:], self.SV_GAS_3_DATA[-50:], 'm-.')
self.ax2.grid(axis='x', color='r')
self.ax2.grid(axis='y', color='r')
# self.ax2.axis.set_color('red')
self.ax2.set_ylim(0, 1000)
self.ax2.tick_params(colors='red')
self.ax2.patch.set_facecolor('#000000')
self.canvas2.draw()
self.topFrame.after(100, self.show_fig)
# left frame 버튼 interface
# 온도 설정 버튼
def sv_temp_btn(self, *_):
self.SV_TEMP_BTN_STATUS *= -1
print('SV_TEMP_BTN_STAUS : ', self.SV_TEMP_BTN_STATUS)
# 온도 자동 운전 버른
def auto_temp_btn(self, *_):
self.AUTO_TEMP_BTN_STATUS *= -1
print('AUTO_TEMP_BTN_STATUS : ', self.AUTO_TEMP_BTN_STATUS)
if self.AUTO_TEMP_BTN_STATUS > 0:
self.auto_temp_button.configure(bg='#C85252', bd=6)
self.fanin_button.configure(state=NORMAL)
self.fanout_button.configure(state=NORMAL)
self.heat_button.configure(state=NORMAL)
elif self.AUTO_TEMP_BTN_STATUS < 0:
self.auto_temp_button.configure(bg='#D0DEF0', bd=6)
self.fanin_button.configure(state=DISABLED)
self.fanout_button.configure(state=DISABLED)
self.heat_button.configure(state=DISABLED)
# 급기팬 버튼
def fanin_btn(self, *_):
self.FANIN_BTN_STATUS *= -1
print('FANIN_BTN_STATUS : ', self.FANIN_BTN_STATUS)
# 배기팬 버튼
def fanout_btn(self, *_):
self.FANOUT_BTN_STATUS *= -1
print('FANOUT_BTN_STATUS : ', self.FANOUT_BTN_STATUS)
# 히트 버튼
def heat_btn(self, *_):
self.HEAT_BTN_STATUS *= -1
print('HEAT_BTN_STATUS : ', self.HEAT_BTN_STATUS)
# 온도 운전 조건 셋팅
def set_auto_temp(self, *_):
if self.SV_TEMP_BTN_STATUS > 0:
self.sv_temp_button.configure(bg='#C85252', bd=6)
self.SV_TEMP_VAL = int(self.sv_temp_entry.get())
elif self.SV_TEMP_BTN_STATUS < 0:
self.sv_temp_button.configure(bg='#D0DEF0', bd=6)
if self.FANIN_BTN_STATUS > 0:
self.fanin_button.configure(text='급기팬 ON', bg='yellow', bd=6)
elif self.FANIN_BTN_STATUS < 0:
self.fanin_button.configure(text='급기팬 Off', bg='#FFFFFF', bd=6)
if self.FANOUT_BTN_STATUS > 0:
self.fanout_button.configure(text='배기팬 ON', bg='yellow', bd=6)
elif self.FANOUT_BTN_STATUS < 0:
self.fanout_button.configure(text='배기팬 Off', bg='#FFFFFF', bd=6)
if self.HEAT_BTN_STATUS > 0:
self.heat_button.configure(text='HEAT ON', bg='yellow', bd=6)
elif self.HEAT_BTN_STATUS < 0:
self.heat_button.configure(text='HEAT Off', bg='#FFFFFF', bd=6)
if self.SV_TEMP_BTN_STATUS > 0 and self.AUTO_TEMP_BTN_STATUS > 0:
if self.SV_TEMP_VAL > self.PV_TEMP_VAL:
self.FANIN_BTN_STATUS = 1
self.FANOUT_BTN_STATUS = 1
self.HEAT_BTN_STATUS = 1
else:
self.FANIN_BTN_STATUS = -1
self.FANOUT_BTN_STATUS = -1
self.HEAT_BTN_STATUS = -1
elif self.AUTO_TEMP_BTN_STATUS < 0:
self.FANIN_BTN_STATUS = -1
self.FANOUT_BTN_STATUS = -1
self.HEAT_BTN_STATUS = -1
if self.FANIN_BTN_STATUS > 0:
#print(self.FANIN_BTN_STATUS)
USB_RELAY('COM3', 9600, 'ON')
if self.FANIN_BTN_STATUS < 0:
#print(self.FANIN_BTN_STATUS)
USB_RELAY('COM3', 9600, 'OFF')
self.leftFrame.after(30, self.set_auto_temp)
# right frame 버튼 interface
#
def sv_gas_btn(self, STATUS):
if STATUS == 'SVG1':
self.SV_GAS_1_BTN_STATUS *= -1
elif STATUS == 'SVG2':
self.SV_GAS_2_BTN_STATUS *= -1
elif STATUS == 'SVG3':
self.SV_GAS_3_BTN_STATUS *= -1
# print('GAS STATUS : ', STATUS)
# 가스 자동 운전
def auto_gas_btn(self, *_):
self.AUTO_GAS_BTN_STATUS *= -1
self.GAS_STATUS = self.SV_GAS_1_BTN
print('AUTO_GAS_BTN_STATUS : ', self.AUTO_GAS_BTN_STATUS)
if self.AUTO_GAS_BTN_STATUS > 0:
self.auto_gas_button.configure(bg='#C85252', bd=6)
self.gas_vv_button.configure(state=NORMAL)
elif self.AUTO_GAS_BTN_STATUS < 0:
self.auto_gas_button.configure(bg='#DEE2E9', bd=6)
self.gas_vv_button.configure(state=DISABLED)
# 가스 밸브
def gas_vv_btn(self, *_):
self.GAS_VV_BTN_STATUS *= -1
print('GAS_VV_BTN_STATUS : ', self.GAS_VV_BTN_STATUS)
# 가스 운전 조건 셋팅
def set_auto_gas(self, *_):
if self.SV_GAS_1_BTN_STATUS > 0:
self.sv_gas_1_button.configure(bg='#C85252', bd=6)
self.SV_GAS_1_VAL = int(self.sv_gas_1_entry.get())
self.SV_GAS_2_BTN_STATUS = -1
self.SV_GAS_3_BTN_STATUS = -1
elif self.SV_GAS_1_BTN_STATUS < 0:
self.sv_gas_1_button.configure(bg='#D0DEF0', bd=6)
if self.SV_GAS_2_BTN_STATUS > 0:
self.sv_gas_2_button.configure(bg='#C85252', bd=6)
self.SV_GAS_2_VAL = int(self.sv_gas_2_entry.get())
self.SV_GAS_1_BTN_STATUS = -1
self.SV_GAS_3_BTN_STATUS = -1
elif self.SV_GAS_2_BTN_STATUS < 0:
self.sv_gas_2_button.configure(bg='#D0DEF0', bd=6)
if self.SV_GAS_3_BTN_STATUS > 0:
self.sv_gas_3_button.configure(bg='#C85252', bd=6)
self.SV_GAS_3_VAL = int(self.sv_gas_3_entry.get())
self.SV_GAS_1_BTN_STATUS = -1
self.SV_GAS_2_BTN_STATUS = -1
elif self.SV_GAS_3_BTN_STATUS < 0:
self.sv_gas_3_button.configure(bg='#D0DEF0', bd=6)
if self.GAS_VV_BTN_STATUS > 0:
self.gas_vv_button.configure(text='가스 밸브 OPEN', bg='yellow', bd=6)
elif self.GAS_VV_BTN_STATUS < 0:
self.gas_vv_button.configure(text='가스 밸브 CLOSE', bg='#FFFFFF', bd=6)
#print('GAS_STATUS : ', self.GAS_STATUS,' AUTO_GAS_BTN_STATUS : ', self.AUTO_GAS_BTN_STATUS,'SV_GAS_1_VAL : ', self.SV_GAS_1_VAL, 'PV_GAS_1_VAL : ', self.PV_GAS_1_VAL)
if self.AUTO_GAS_BTN_STATUS > 0:
if self.SV_GAS_1_VAL > self.PV_GAS_1_VAL and self.SV_GAS_1_BTN_STATUS > 0:
self.GAS_VV_BTN_STATUS = 1
if self.SV_GAS_2_VAL > self.PV_GAS_2_VAL and self.SV_GAS_2_BTN_STATUS > 0:
self.GAS_VV_BTN_STATUS = 1
if self.SV_GAS_3_VAL > self.PV_GAS_3_VAL and self.SV_GAS_3_BTN_STATUS > 0:
self.GAS_VV_BTN_STATUS = 1
elif self.AUTO_GAS_BTN_STATUS < 0:
self.GAS_VV_BTN_STATUS = -1
self.rightFrame.after(50, self.set_auto_gas)
def win():
window = Tk()
Dashboard(window)
window.mainloop()
if __name__ == '__main__':
#conn = pymysql.connect(host='127.0.0.1', user='root', password='1234', db='chamber_db')
#cursor = conn.cursor()
win()
728x90
'파이썬(PYTHON)' 카테고리의 다른 글
CharGPT를 활용한 자동차 구성품 트리 구조 (0) | 2023.03.11 |
---|---|
파이썬을 이용한 위치에 따른 점들의 랜덤한 움직임에 대한 고찰 (경계면 구속 조건시) (0) | 2022.10.12 |
물분자 운동 simulation (0) | 2022.09.01 |
입자 운동 시뮬레이션 (particle movement simulation) (0) | 2022.09.01 |
분리막 형상 (대칭 vs 비대칭) 따른 확산 고찰과 그 결과 (0) | 2022.08.18 |