first commit

This commit is contained in:
2026-08-04 14:31:11 +09:00
commit 4176f04a61
25 changed files with 5294 additions and 0 deletions
+5
View File
@@ -0,0 +1,5 @@
__pycache__/
*.py[cod]
.venv/
venv/
.env
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+3257
View File
File diff suppressed because it is too large Load Diff
+113
View File
@@ -0,0 +1,113 @@
from flask import Flask, render_template, request, jsonify
import sys
import os
from api_routes import api_routes
import webview
from waitress import serve
if getattr(sys, 'frozen', False):
template_folder = os.path.join(sys._MEIPASS, 'templates')
app = Flask(__name__, template_folder=template_folder, static_url_path='/static')
else:
app = Flask(__name__, static_url_path='/static')
#app = Flask(__name__, static_url_path='/static')
app.register_blueprint(api_routes) # 분리된 API 라우트 등록
@app.route('/')
def index():
return render_template('layer.html')
@app.route('/progress')
def progess():
return render_template('progress.html')
@app.route('/oneLineProgress')
def oneLineProgess():
return render_template('oneLineProgress.html')
# 종료 함수
def shutdown_waitress_server():
print("Shutting down the server...")
os._exit(0)
def destroy(window):
print('Destroying window..')
window.destroy()
print('Destroyed!')
@app.route('/shutdown', methods=['POST'])
def shutdown():
destroy(test)
shutdown_waitress_server()
return 'Server shutting down'
port=5000
# if __name__ == "__main__":
# app.run(port=port, debug=True)
test = webview.create_window('Palletizing', app)
if __name__ == "__main__":
webview.start()
serve(app, host="0.0.0.0", port=port)
# from flask import Flask, render_template, request
# import sys
# import os
# from api_routes import api_routes
# import webview
# from waitress import serve
# import threading
# # Flask 애플리케이션 설정
# if getattr(sys, 'frozen', False):
# template_folder = os.path.join(sys._MEIPASS, 'templates')
# app = Flask(__name__, template_folder=template_folder, static_url_path='/static')
# else:
# app = Flask(__name__, static_url_path='/static')
# app.register_blueprint(api_routes)
# @app.route('/')
# def index():
# return render_template('layer.html')
# @app.route('/progress')
# def progress():
# return render_template('progress.html')
# @app.route('/oneLineProgress')
# def oneLineProgress():
# return render_template('oneLineProgress.html')
# # 종료 함수
# def shutdown_server():
# print("Shutting down the server...")
# os._exit(0)
# # 종료 라우트 설정
# @app.route('/shutdown', methods=['POST'])
# def shutdown():
# print("Shutdown request received.")
# destroy(test) # Webview 윈도우 종료
# shutdown_server() # Waitress 서버와 프로그램 종료
# return 'Server shutting down...'
# # Webview 윈도우 종료 함수
# def destroy(window):
# print('Destroying window..')
# window.destroy()
# print('Destroyed!')
# # 서버 실행
# port = 5000
# test = webview.create_window('Palletizing', app)
# if __name__ == "__main__":
# # Webview와 Waitress 서버를 별도 스레드로 실행
# server_thread = threading.Thread(target=lambda: serve(app, host="0.0.0.0", port=port))
# server_thread.start()
# webview.start()
+133
View File
@@ -0,0 +1,133 @@
# import socket
# import time
# def handle_client(client_socket, client_address):
# print(f"Connected by {client_address}")
# with client_socket:
# while True:
# try:
# recv_data = client_socket.recv(1024).decode()
# if not recv_data:
# print(f"Connection closed by {client_address}")
# break
# print(f"Received from {client_address}: {recv_data}")
# if recv_data == 'get <socket_num>':
# send_data = '<socket_num><2>'
# client_socket.send(send_data.encode())
# if recv_data == 'get <socket_str>':
# send_data = '<socket_str><"b">'
# client_socket.send(send_data.encode())
# if recv_data == 'get <socket_arr>':
# send_data = '<socket_arr><[1,1,1,1,1,1]>'
# client_socket.send(send_data.encode())
# if recv_data == 'get #real#6#':
# send_data = '[2,2,2,2,2,2]'
# client_socket.send(send_data.encode())
# if recv_data == 'palletType':
# send_data = '<palletType><1>'
# client_socket.send(send_data.encode())
# except ConnectionResetError as e:
# print(f"Connection closed by {client_address}")
# break
# def start_server(host='0.0.0.0', port=12345):
# server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# server_socket.bind((host, port))
# server_socket.listen()
# server_socket.settimeout(1)
# print(f"Server listening on {host}:{port}")
# try:
# while True:
# try:
# client_socket, client_address = server_socket.accept()
# handle_client(client_socket, client_address)
# except socket.timeout:
# continue
# except KeyboardInterrupt:
# print("Server shutting down...")
# finally:
# server_socket.close()
# if __name__ == "__main__":
# start_server()
import socket
import time
def handle_client(client_socket, client_address):
print(f"Connected by {client_address}")
with client_socket:
while True:
try:
recv_data = client_socket.recv(1024).decode()
if not recv_data:
print(f"Connection closed by {client_address}")
return False # 클라이언트 종료 신호 반환
print(f"Received from {client_address}: {recv_data}")
if recv_data == 'get <socket_num>':
send_data = '<socket_num><2>'
client_socket.send(send_data.encode())
if recv_data == 'get <socket_str>':
send_data = '<socket_str><"b">'
client_socket.send(send_data.encode())
if recv_data == 'get <socket_arr>':
send_data = '<socket_arr><[1,1,1,1,1,1]>'
client_socket.send(send_data.encode())
if recv_data == 'get #real#6#':
send_data = '[2,2,2,2,2,2]'
client_socket.send(send_data.encode())
if recv_data == 'palletType':
send_data = '<palletType><1>'
client_socket.send(send_data.encode())
except ConnectionResetError:
print(f"Connection closed by {client_address}")
return False # 클라이언트 종료 신호 반환
return True # 클라이언트 정상 처리 완료 신호 반환
def start_server(host='0.0.0.0', port=12345):
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind((host, port))
server_socket.listen()
server_socket.settimeout(1)
print(f"Server listening on {host}:{port}")
try:
while True:
try:
client_socket, client_address = server_socket.accept()
connection_active = handle_client(client_socket, client_address)
if not connection_active:
print("Client disconnected. Shutting down server...")
break # 루프 종료
except socket.timeout:
continue
except KeyboardInterrupt:
print("Server shutting down...")
finally:
server_socket.close()
print("Server socket closed.")
if __name__ == "__main__":
start_server()
+258
View File
@@ -0,0 +1,258 @@
.container {
margin: 20px;
border: solid 1px black;
}
main {
display: grid;
grid-template-columns: 1fr 1.5fr 1fr;
}
.leftContainer {
display: flex;
flex-direction: column;
align-items: center;
border: solid 1px black;
}
.leftbuttonBox {
margin-top: 20px;
display: flex;
flex-direction: column;
align-items: center;
}
.leftContainer button {
margin-top: 20px;
width: 200px;
height: 72px;
}
.leftContainer button.selected {
color: rgb(54, 54, 54);
background-color: rgb(61, 230, 69);
}
.middleContainer {
display: grid;
grid-template-rows: auto;
border: solid 1px black;
}
.middleBoxContainer {
margin-bottom: 20px;
}
.buttonBox {
display: flex;
flex-direction: column;
margin-top: 30px;
}
.buttonBox button {
padding: 15px 20px;
font-size: 32px;
}
.buttonWithImage {
display: flex;
align-items: center;
margin-bottom: 10px;
}
.buttonWithImage img {
width: 140px;
height: 140px;
margin-right: 10px;
margin-left: 17px;
}
.buttonBox button.selected {
color: rgb(54, 54, 54);
background-color: rgb(61, 230, 69);
}
.rightContainer {
display: grid;
grid-template-rows: 60px 60px 238px 60px 60px;
border: solid 1px black;
}
button {
margin: 0 20px;
margin-bottom: 10px;
padding: 10px 20px;
font-size: 32px;
font-weight: bold;
color: rgb(54, 54, 54);
background-color: #3396ff;
border: solid 2px black;
cursor: pointer;
transition: background-color 0.3s ease;
}
button:hover {
background-color: #0056b3;
}
#sendPaletteInfo {
margin: 0;
width: 100%;
height: 72px;
margin-bottom: 20px;
background-color: #3396ff;
}
#startRobotButton {
margin: 0;
width: 100%;
height: 72px;
margin-bottom: 55px;
margin-top: 10px;
padding: 10px 15px;
background-color: #3396ff;
}
#startRobotButton:disabled {
background-color: gray;
color: white;
cursor: not-allowed;
}
#woodenFlatPallet:disabled {
background-color: gray;
color: white;
cursor: not-allowed;
}
#exitAppButton {
margin: 0;
width: 100%;
color: rgb(54, 54, 54);
background-color: rgb(252, 101, 101);
padding: 10px;
cursor: pointer;
font-size: 22px;
}
#resetButton {
margin: 0;
width: 100%;
color: rgb(54, 54, 54);
background-color: rgb(252, 169, 101);
padding: 10px;
cursor: pointer;
font-size: 22px;
}
#bookHeight {
width: 100px;
height: 40px;
margin-top: 10px;
padding: 10px;
font-size: 19px;
border: 1px solid #ccc;
border-radius: 5px;
}
#palletHeight {
width: 180px;
height: 40px;
margin-top: 10px;
padding: 10px;
font-size: 19px;
border: 1px solid #ccc;
border-radius: 5px;
}
.inputContainer {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.inputBox {
display: flex;
}
p {
display: flex;
justify-content: center;
align-items: center;
width: 60px;
height: 40px;
margin-top: 10px;
padding: 10px;
font-size: 19px;
border: 1px solid #ccc;
border-radius: 5px;
}
#bookHeight::placeholder,
#palletHeight::placeholder {
color: #999;
}
#bookHeightTitle,
#palletHeightTitle {
margin-top: 20px;
font-size: 19px;
font-weight: 600;
}
html, body {
margin: 0;
padding: 0;
height: 100%;
width: 100%;
justify-content: center;
align-items: center;
position: relative;
overflow: hidden;
}
#alert {
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.3);
position: absolute;
justify-content: center;
display: none;
}
#alertBox{
width: 80%;
height: 50%;
background-color: white;
display: flex;
align-self: center;
border-radius: 10px;
border:2px solid black;
justify-content: center;
align-items: center;
flex-direction: column;
}
#loader {
border: 16px solid #f3f3f3; /* Light gray */
border-top: 16px solid #3498db; /* Blue */
border-radius: 50%;
width: 100px;
height: 100px;
animation: spin 1s linear infinite;
margin: 10px auto; /* Center the loader */
}
#alert_msg {
font-size: 32px;
font-weight: bold;
color: rgb(54, 54, 54);
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
+249
View File
@@ -0,0 +1,249 @@
.container {
margin: 20px;
border: solid 1px black;
}
main {
display: grid;
grid-template-columns: 2fr 1.5fr;
}
.leftContainer {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
border: solid 1px black;
min-height: 90vh;
}
.gridContainer {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 10px;
width: 100%;
max-width: 380px;
margin: 0 auto;
margin-top: 0px;
}
.gridItem {
width: 180px;
height: 120px;
background-color: #f0f0f0;
display: flex;
align-items: center;
justify-content: center;
border: 1px solid #ccc;
cursor: pointer;
font-size: 45px;
font-weight: 600;
}
.gridItem.selected {
background-color: #34b800;
color: #fff;
}
.gridItem.disabled {
pointer-events: none;
opacity: 0.5; /* 비활성화 상태를 시각적으로 나타내기 위해 투명도를 줄입니다 */
}
.buttonContainer {
margin-top: 10px;
display: flex;
flex-direction: column;
align-items: center;
}
.buttonContainer button {
width: 330px;
height: 72px;
margin-top: 10px;
}
.buttonContainer button:disabled {
background-color: gray;
color: white;
cursor: not-allowed;
}
.rightContainer {
border: solid 1px black;
display: grid;
grid-template-rows: repeat(4, 60px);
}
#finishAction {
margin: 0;
width: 100%;
height: 70px;
}
#terminateAction {
margin: 0;
width: 100%;
height: 70px;
background-color: #ff3c00;
}
#currentLayerIndexDisplay {
font-weight: 600;
font-size: 23px;
}
button {
margin: 0 20px;
margin-bottom: 10px;
padding: 10px 20px;
font-size: 37px;
font-weight: 700;
color: rgb(54, 54, 54);
background-color: #3396ff;
border: solid 20x black;
cursor: pointer;
transition: background-color 0.3s ease;
}
#exitAppButton {
width: 100%;
margin: 0;
height: 60px;
border-radius: 0px;
color: rgb(54, 54, 54);
background-color: rgb(252, 101, 101);
font-size: 22px;
border: solid 1px black;
}
.buttonBox {
display: flex;
flex-wrap: wrap;
width: 100%;
}
.buttonBox button {
margin: 0;
height: 60px;
width: 50%;
border-radius: 0px;
font-size: 22px;
border: solid 1px black;
}
#stackerStartButton,
#upDownOnButton,
#leftRightOnButton {
color: rgb(54, 54, 54);
background-color: rgb(61, 230, 69);
}
#stackerStopButton,
#upDownOffButton,
#leftRightOffButton {
color: rgb(54, 54, 54);
background-color: rgb(252, 169, 101);
}
.inputContainer {
display: flex;
flex-direction: column;
justify-content: end;
align-items: center;
}
.inputBox {
display: flex;
}
p {
display: flex;
justify-content: center;
align-items: center;
width: 60px;
height: 35px;
margin-top: 10px;
padding: 10px;
font-size: 19px;
border: 1px solid #ccc;
border-radius: 5px;
}
span {
font-size: 21px;
font-weight: 600;
}
#bookHeight {
width: 100px;
height: 35px;
margin-top: 10px;
padding: 10px;
font-size: 19px;
border: 1px solid #ccc;
border-radius: 5px;
}
#bookHeight::placeholder {
color: #999;
}
#bookHeightTitle {
margin-top: 10px;
font-size: 19px;
font-weight: 600;
}
/* 팝업을 화면 중앙에 표시하기 위한 스타일 */
#bookCountPopup {
display: none; /* 기본적으로 보이지 않음 */
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%); /* 창을 중앙에 위치하게 함 */
width: 300px;
padding: 20px;
background-color: white;
box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.2);
border-radius: 8px;
z-index: 1001;
text-align: center;
}
/* 배경을 어둡게 처리 */
#popupOverlay {
display: none; /* 기본적으로 보이지 않음 */
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
z-index: 1000;
}
/* 제출 버튼 스타일 */
#submitBookCount {
margin-top: 15px;
padding: 10px 20px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
}
#submitBookCount:hover {
background-color: #45a049;
}
/* 입력 필드 스타일 */
#bookCountInput {
margin-top: 10px;
padding: 8px;
width: 80%;
border: 1px solid #ccc;
border-radius: 5px;
}
+109
View File
@@ -0,0 +1,109 @@
.container {
margin: 0;
margin: 20px;
border: solid 1px black;
}
main {
display: grid;
grid-template-columns: 2fr 1fr;
}
.leftContainer {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
min-height: 90vh;
border: solid 1px black;
}
.rightContainer {
border: solid 1px black;
}
.msg {
margin-top: 70px;
font-size: 45px;
font-weight: 700;
}
#finishAction {
margin-top: 20px;
width: 343px;
height: 74px;
}
#terminateAction {
margin-top: 25px;
width: 343px;
height: 74px;
background-color: rgb(252, 101, 101);
}
button {
margin: 0 20px;
margin-bottom: 10px;
padding: 10px 20px;
font-size: 38px;
font-weight: 700;
color: rgb(54, 54, 54);
background-color: #3396ff;
border: solid 2px black;
cursor: pointer;
transition: background-color 0.3s ease;
}
p {
font-size: 30px;
font-weight: 600;
}
span {
font-size: 30px;
font-weight: 600;
}
.rightContainer {
display: grid;
grid-template-rows: repeat(4, 60px);
}
.buttonBox {
display: flex;
height: 70px;
}
#exitAppButton {
width: 100%;
margin: 0;
height: 60px;
border-radius: 0px;
color: rgb(54, 54, 54);
background-color: rgb(252, 101, 101);
font-size: 22px;
border: solid 1px black;
}
.buttonBox button {
margin: 0;
height: 60px;
width: 16vw;
border-radius: 0px;
font-size: 22px;
border: solid 1px black;
}
#stackerStartButton,
#upDownOnButton,
#leftRightOnButton {
color: rgb(54, 54, 54);
background-color: rgb(61, 230, 69);
}
#stackerStopButton,
#upDownOffButton,
#leftRightOffButton {
color: rgb(54, 54, 54);
background-color: rgb(252, 169, 101);
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

+275
View File
@@ -0,0 +1,275 @@
const bookCenterPosition_Wheel = [
{ x: 450.5, y: 105 },
{ x: 450.5, y: 320 },
{ x: 148.5, y: 105 },
{ x: 148.5, y: 320 },
];
const bookCenterPosition_Flat = [
{ x: 750, y: 148.5 },
{ x: 535, y: 148.5 },
{ x: 320, y: 148.5 },
{ x: 105, y: 148.5 },
{ x: 750, y: 450.5 },
{ x: 535, y: 450.5 },
{ x: 320, y: 450.5 },
{ x: 105, y: 450.5 },
];
const woodenWheelPallet = {
palletWidth: 620,
palletLength: 442,
palletHeight: 653,
bookCenterPosition_Wheel,
};
const ironWheelPallet = {
palletWidth: 620,
palletLength: 442,
palletHeight: 633,
bookCenterPosition_Wheel,
};
const woodenFlatPallet = {
palletWidth: 878,
palletLength: 638,
palletHeight: 148,
bookCenterPosition_Flat,
};
const pallets = {
woodenWheelPallet,
ironWheelPallet,
woodenFlatPallet,
};
document.addEventListener('DOMContentLoaded', function () {
const sendPaletteInfoButton = document.getElementById('sendPaletteInfo');
const startRobotMove = document.getElementById('startRobotButton');
const resetButton = document.getElementById('resetButton');
const exitAppButton = document.getElementById('exitAppButton');
const palletHeightInput = document.getElementById('palletHeight');
const palletHeightTitle = document.getElementById('palletHeightTitle');
const stackingButtons = document.querySelectorAll('.leftbuttonBox button');
let stackingSelected = false;
const palletButtons = document.querySelectorAll('.buttonBox button');
let selectedPalletId = null;
const woodenFlatPalletButton = document.getElementById('woodenFlatPallet');
let isStartclicked = false;
function customAlert(msg) {
const alert = document.getElementById('alert');
const text = document.getElementById('alert_msg');
if (msg) {
alert.style.display = 'flex';
text.innerText = msg;
} else {
alert.style.display = 'none';
text.innerText = '';
}
}
function logAction(message) {
fetch('/writelog', {
method: 'POST',
headers: {
'Content-Type': 'text/plain',
},
body: message,
})
.then((response) => {
if (!response.ok) {
throw new Error('Failed to write log.');
}
return response.text();
})
.then((data) => {
console.log(`Log result: ${data}`);
})
.catch((error) => console.error('Error writing log:', error));
}
stackingButtons.forEach((button) => {
button.addEventListener('click', () => {
stackingButtons.forEach((btn) => btn.classList.remove('selected'));
button.classList.add('selected');
stackingSelected = true;
stackingButtons.forEach((btn) => btn.classList.remove('disabled'));
// pallet 버튼의 선택 상태를 해제
palletButtons.forEach((btn) => {
btn.classList.remove('selected');
btn.disabled = false; // pallet 버튼을 활성화 상태로 되돌림
});
selectedPalletId = null;
sendPaletteInfoButton.disabled = true;
if (button.id === 'oneLineButton') {
woodenFlatPalletButton.classList.add('disabled');
woodenFlatPalletButton.disabled = true;
palletHeightInput.style.display = 'none';
palletHeightTitle.style.display = 'none';
} else if (button.id === 'normalButton') {
woodenFlatPalletButton.classList.remove('disabled');
woodenFlatPalletButton.disabled = false;
}
logAction(`FE : Stacking button ${button.id} clicked - layer.js`);
});
});
palletButtons.forEach((button) => {
button.addEventListener('click', () => {
if (!stackingSelected) {
alert('⛔ 쌓기 버튼을 먼저 선택해주세요!');
return;
}
palletButtons.forEach((btn) => btn.classList.remove('selected'));
button.classList.add('selected');
selectedPalletId = button.id;
sendPaletteInfoButton.disabled = false;
if (selectedPalletId === 'woodenFlatPallet') {
palletHeightInput.style.display = 'block';
palletHeightTitle.style.display = 'block';
} else {
palletHeightInput.style.display = 'none';
palletHeightTitle.style.display = 'none';
}
logAction(`FE : Pallet button ${button.id} clicked - layer.js`);
});
});
sendPaletteInfoButton.addEventListener('click', function () {
let palletHeight = pallets[selectedPalletId].palletHeight; // 기본 높이를 사용
if (selectedPalletId === 'woodenFlatPallet') {
palletHeight = parseFloat(palletHeightInput.value);
if (isNaN(palletHeight) || palletHeight <= 0) {
alert('⛔ 유효한 팔레트 높이를 입력해주세요!');
return;
}
}
if (selectedPalletId) {
const selectedPallet = pallets[selectedPalletId];
selectedPallet.palletHeight = palletHeight; // update the pallet height
fetch('/update_pallet', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(selectedPallet),
})
.then((response) => {
if (!response.ok) {
throw new Error('Failed to send palette info.');
}
return response.text();
})
.then((data) => {
if (data == 'Success') {
startRobotMove.disabled = false;
sendPaletteInfoButton.style.backgroundColor = '#34b800';
isStartclicked = false;
logAction('FE : update_pallet api sent successfully - layer.js');
}
})
.catch((error) => console.error('Error sending palette info:', error));
} else {
alert('⛔ 팔레트를 선택해주세요!');
}
});
startRobotMove.addEventListener('click', function () {
logAction('FE : startRobotMove button clicked - layer.js');
if (selectedPalletId) {
let confirmResult = confirm('로봇 동작을 시작하시겠습니까?');
if (confirmResult && isStartclicked == false) {
isStartclicked = true;
const selectedPallet = pallets[selectedPalletId];
const selectedStackingButton = document.querySelector(
'.leftbuttonBox .selected'
);
let btnId = '';
if (selectedStackingButton.id === 'normalButton') {
btnId = 'normalButton';
window.location.href = '/progress';
} else if (selectedStackingButton.id === 'oneLineButton') {
btnId = 'oneLineButton';
window.location.href = '/oneLineProgress';
}
const data = { selectedPallet, btnId, selectedPalletId };
customAlert('로봇 동작을 시작합니다...');
fetch('/robot_start', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
})
// 지금 현재 상황 (2024.10.10 문제점 : .then아래로 안내려옴 문제 해결 필요 시급)
.then((response) => {
if (!response.ok) {
throw new Error('Failed to send palette info.');
}
return response.text();
})
.then((data) => {
console.log(data);
logAction(
'FE : robot_start function(server) finish successfully - layer.js'
);
})
.catch((error) =>
console.error('Error sending palette info:', error)
);
}
} else {
alert('⛔ 팔레트를 선택해주세요!');
}
});
resetButton.addEventListener('click', function () {
fetch('/reset_data', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ message: 'Reset button clicked' }),
})
.then((response) => {
if (!response.ok) {
throw new Error('Failed to send reset data.');
}
return response.text();
})
.then((data) => {
if (data == 'Success') {
logAction('FE : reset_data api sent successfully - layer.js');
}
})
.catch((error) => console.error('Error sending reset data:', error));
});
exitAppButton.addEventListener('click', function () {
fetch('/shutdown', {
method: 'POST',
})
.then((response) => response.text())
.then((data) => {
console.log(data);
logAction('FE : shutdown api sent successfully - layer.js');
})
.catch((error) => console.error('Error shutting down:', error));
});
});
+418
View File
@@ -0,0 +1,418 @@
document.addEventListener('DOMContentLoaded', function () {
const terminateAction = document.getElementById('terminateAction');
const finishAction = document.getElementById('finishAction');
const gridItems = document.querySelectorAll('.gridItem');
const runButton = document.getElementById('run');
const stopButton = document.getElementById('stop');
const exitAppButton = document.getElementById('exitAppButton');
const stackerStartButton = document.getElementById('stackerStartButton');
const stackerStopButton = document.getElementById('stackerStopButton');
const upDownOnButton = document.getElementById('upDownOnButton');
const upDownOffButton = document.getElementById('upDownOffButton');
const leftRightOnButton = document.getElementById('leftRightOnButton');
const leftRightOffButton = document.getElementById('leftRightOffButton');
let selectedGridId = null; // 선택된 그리드 ID
let intervalId = null; // setInterval ID
let isRunning = false; // 한줄쌓기 실행 상태
// 초기 상태 설정
stopButton.disabled = true;
runButton.disabled = true;
function logAction(message) {
fetch('/writelog', {
method: 'POST',
headers: {
'Content-Type': 'text/plain',
},
body: message,
})
.then((response) => {
if (!response.ok) {
throw new Error('Failed to write log.');
}
return response.text();
})
.then((data) => {
console.log(`Log result: ${data}`);
})
.catch((error) => console.error('Error writing log:', error));
}
// JSON 파일에서 데이터 로드
function fetchJsonData() {
fetch('/static/json/oneLine.json')
.then((response) => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then((data) => {
updateGridStatus(data);
})
.catch((error) => console.error('Error loading JSON:', error));
}
// 초기 데이터 로드
fetchJsonData();
// 그리드 상태 업데이트 함수
function updateGridStatus(data) {
const grids = ['grid1', 'grid2', 'grid3', 'grid4'];
// 그리드 비활성화 초기화
gridItems.forEach((item) => item.classList.remove('disabled'));
grids.forEach((grid) => {
if (data[grid].currentLayerHeight > 720) {
document.getElementById(grid).classList.add('disabled');
}
});
if (data.grid4.currentLayerHeight > data.grid2.currentLayerHeight) {
document.getElementById('grid2').classList.add('disabled');
}
if (data.grid3.currentLayerHeight > data.grid1.currentLayerHeight) {
document.getElementById('grid1').classList.add('disabled');
}
// 선택된 그리드의 currentLayerIndex 출력
if (selectedGridId) {
const selectedGridData = data[selectedGridId];
document.getElementById(
'currentLayerIndexDisplay'
).textContent = `현재 쌓고 있는 단 : ${
selectedGridData.currentLayerIndex + 1
}`;
}
// 한줄쌓기 실행 중인 경우 그리드 비활성화 유지
if (isRunning) {
toggleGridItems(true);
}
}
// 그리드 아이템 클릭 핸들러
function handleGridItemClick(event) {
const item = event.currentTarget;
if (item.classList.contains('disabled')) {
return; // 클릭 이벤트 무시
}
gridItems.forEach((btn) => btn.classList.remove('selected'));
item.classList.add('selected');
selectedGridId = item.id;
checkRunButtonState();
}
gridItems.forEach((item) => {
item.addEventListener('click', handleGridItemClick);
});
// 한줄쌓기 실행 버튼 클릭 핸들러
runButton.addEventListener('click', function () {
runButton.disabled = true;
stopButton.disabled = false;
isRunning = true;
toggleGridItems(true);
intervalId = setInterval(fetchJsonData, 5000);
fetch('/one_line_run', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
message: 'one_line_run',
gridId: selectedGridId, // 선택된 그리드 아이템의 ID 전송
}),
})
.then((response) => {
if (!response.ok) {
throw new Error('one_line_run failed.');
}
return response.text();
})
.then((data) => {
console.log(data);
logAction(
'FE : one_line_run api sent successfully - oneLineProgress.js'
);
})
.catch((error) =>
console.error('Error sending one_line_run action:', error)
);
});
// 한줄쌓기 멈춤 버튼 클릭 핸들러
stopButton.addEventListener('click', function () {
runButton.disabled = false;
stopButton.disabled = true;
isRunning = false;
toggleGridItems(false);
clearGridSelection();
// JSON 데이터 주기적으로 가져오기 중지
clearInterval(intervalId);
// 멈춤 버튼 클릭 시 JSON 데이터 다시 로드
fetchJsonData();
fetch('/one_line_stop', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ message: 'one_line_stop' }),
})
.then((response) => {
if (!response.ok) {
throw new Error('one_line_stop failed.');
}
return response.text();
})
.then((data) => {
console.log(data);
logAction(
'FE : one_line_stop api sent successfully - oneLineProgress.js'
);
})
.catch((error) =>
console.error('Error sending one_line_stop action:', error)
);
});
// 한줄쌓기 실행 버튼 활성화 여부 확인 함수
function checkRunButtonState() {
const anyGridSelected = Array.from(gridItems).some((item) =>
item.classList.contains('selected')
);
runButton.disabled = !anyGridSelected;
}
// 그리드 아이템 활성화/비활성화 함수
function toggleGridItems(disable) {
gridItems.forEach((item) => item.classList.toggle('disabled', disable));
}
// 그리드 아이템 선택 해제 함수
function clearGridSelection() {
gridItems.forEach((item) => item.classList.remove('selected'));
checkRunButtonState(); // 선택 해제 후 버튼 상태 재확인
}
finishAction.addEventListener('click', function () {
finishAction.disabled = true;
fetch('/robot_immediately_take_book', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
immediately_take_book: 'robot_immediately_take_book',
}),
})
.then((response) => {
if (!response.ok) {
throw new Error('immediately_take_book failed.');
}
return response.text();
})
.then((data) => {
console.log(data);
logAction(
'FE : robot_immediately_take_book api sent successfully - oneLineProgress.js'
);
})
.catch((error) =>
console.error('Error Sending robot finish action:', error)
)
.finally(() => {
setTimeout(() => {
finishAction.disabled = false;
}, 10000);
});
});
terminateAction.addEventListener('click', function () {
fetch('/robot_finish_action', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ action: 'robot_finish_action' }),
})
.then((response) => {
if (!response.ok) {
throw new Error('Failed to send robot finish action.');
}
return response.text();
})
.then((data) => {
console.log(data);
logAction(
'FE : terminateAction api sent successfully - oneLineProgress.js'
);
window.location.href = '/';
})
.catch((error) =>
console.error('Error sending robot finish action:', error)
);
});
exitAppButton.addEventListener('click', function () {
fetch('/shutdown', {
method: 'POST',
})
.then((response) => response.text())
.then((data) => {
console.log(data);
logAction('FE : shutdown api sent successfully - oneLineProgress.js');
})
.catch((error) => console.error('Error shutting down:', error));
});
stackerStartButton.addEventListener('click', function () {
fetch('/stacker_start', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ action: 'stacker_start' }),
})
.then((response) => {
if (!response.ok) {
throw new Error('Failed to send stacker start.');
}
return response.text();
})
.then((data) => {
console.log(data);
logAction(
'FE : stacker_start api sent successfully - oneLineProgress.js'
);
})
.catch((error) => console.error('Error sending stacker start:', error));
});
stackerStopButton.addEventListener('click', function () {
fetch('/stacker_stop', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ action: 'stacker_stop' }),
})
.then((response) => {
if (!response.ok) {
throw new Error('Failed to send stacker stop.');
}
return response.text();
})
.then((data) => {
console.log(data);
logAction(
'FE : stacker_stop api sent successfully - oneLineProgress.js'
);
})
.catch((error) => console.error('Error sending stacker stop:', error));
});
upDownOnButton.addEventListener('click', function () {
fetch('/updown_on', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ action: 'updown_on' }),
})
.then((response) => {
if (!response.ok) {
throw new Error('Failed to send updown on');
}
return response.text();
})
.then((data) => {
console.log(data);
logAction('FE : updown_on api sent successfully - oneLineProgress.js');
})
.catch((error) => console.error('Error sending updown on:', error));
});
upDownOffButton.addEventListener('click', function () {
fetch('/updown_off', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ action: 'updown_off' }),
})
.then((response) => {
if (!response.ok) {
throw new Error('Failed to send updown off');
}
return response.text();
})
.then((data) => {
console.log(data);
logAction('FE : updown_off api sent successfully - oneLineProgress.js');
})
.catch((error) => console.error('Error sending updown off:', error));
});
leftRightOnButton.addEventListener('click', function () {
fetch('/leftright_on', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ action: 'leftright_on' }),
})
.then((response) => {
if (!response.ok) {
throw new Error('Failed to send leftright on');
}
return response.text();
})
.then((data) => {
console.log(data);
logAction(
'FE : leftright_on api sent successfully - oneLineProgress.js'
);
})
.catch((error) => console.error('Error sending leftright on:', error));
});
leftRightOffButton.addEventListener('click', function () {
fetch('/leftright_off', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ action: 'leftright_off' }),
})
.then((response) => {
if (!response.ok) {
throw new Error('Failed to send leftright off');
}
return response.text();
})
.then((data) => {
console.log(data);
logAction(
'FE : leftright_off api sent successfully - oneLineProgress.js'
);
})
.catch((error) => console.error('Error sending leftright off:', error));
});
});
+255
View File
@@ -0,0 +1,255 @@
document.addEventListener('DOMContentLoaded', function () {
const terminateAction = document.getElementById('terminateAction');
const finishAction = document.getElementById('finishAction');
const exitAppButton = document.getElementById('exitAppButton');
const stackerStartButton = document.getElementById('stackerStartButton');
const stackerStopButton = document.getElementById('stackerStopButton');
const upDownOnButton = document.getElementById('upDownOnButton');
const upDownOffButton = document.getElementById('upDownOffButton');
const leftRightOnButton = document.getElementById('leftRightOnButton');
const leftRightOffButton = document.getElementById('leftRightOffButton');
function logAction(message) {
fetch('/writelog', {
method: 'POST',
headers: {
'Content-Type': 'text/plain',
},
body: message,
})
.then((response) => {
if (!response.ok) {
throw new Error('Failed to write log.');
}
return response.text();
})
.then((data) => {
console.log(`Log result: ${data}`);
})
.catch((error) => console.error('Error writing log:', error));
}
// 인덱스 표시 업데이트 함수
function updateIndexDisplay() {
fetch('/static/json/cache.json')
.then((response) => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then((data) => {
document.getElementById('currentDataIndex').textContent =
data.currentDataIndex + 1;
document.getElementById('currentLayerIndex').textContent =
data.currentLayerIndex + 1;
console.log(data.currentDataIndex, data.currentLayerIndex);
})
.catch((error) => console.error('Error loading cache.json:', error));
}
// 페이지 로드 시 초기 인덱스 표시 업데이트
updateIndexDisplay();
// 일정 시간마다 인덱스 표시 업데이트
intervalId = setInterval(updateIndexDisplay, 5000); // 5초마다 업데이트
finishAction.addEventListener('click', function () {
finishAction.disabled = true;
fetch('/robot_immediately_take_book', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
immediately_take_book: 'robot_immediately_take_book',
}),
})
.then((response) => {
if (!response.ok) {
throw new Error('immediately_take_book failed.');
}
return response.text();
})
.then((data) => {
console.log(data);
logAction(
'FE : robot_immediately_take_book api sent successfully - progress.js'
);
})
.catch((error) =>
console.error('Error Sending robot finish action:', error)
).finally(() => {
setTimeout(() => {
finishAction.disabled = false;
}, 10000);
})
});
terminateAction.addEventListener('click', function () {
fetch('/robot_finish_action', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ action: 'robot_finish_action' }),
})
.then((response) => {
if (!response.ok) {
throw new Error('Failed to send robot finish action.');
}
return response.text();
})
.then((data) => {
console.log(data);
clearInterval(intervalId);
logAction(
'FE : robot_finish_action api sent successfully - progress.js'
);
window.location.href = '/';
})
.catch((error) =>
console.error('Error sending robot finish action:', error)
);
});
exitAppButton.addEventListener('click', function () {
fetch('/shutdown', {
method: 'POST',
})
.then((response) => response.text())
.then((data) => {
console.log(data);
logAction('FE : shutdown api sent successfully - progress.js');
})
.catch((error) => console.error('Error shutting down:', error));
});
stackerStartButton.addEventListener('click', function () {
fetch('/stacker_start', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ action: 'stacker_start' }),
})
.then((response) => {
if (!response.ok) {
throw new Error('Failed to send stacker start.');
}
return response.text();
})
.then((data) => {
console.log(data);
logAction('FE : stacker_start api sent successfully - progress.js');
})
.catch((error) => console.error('Error sending stacker start:', error));
});
stackerStopButton.addEventListener('click', function () {
fetch('/stacker_stop', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ action: 'stacker_stop' }),
})
.then((response) => {
if (!response.ok) {
throw new Error('Failed to send stacker stop.');
}
return response.text();
})
.then((data) => {
console.log(data);
logAction('FE : stacker_stop api sent successfully - progress.js');
})
.catch((error) => console.error('Error sending stacker stop:', error));
});
upDownOnButton.addEventListener('click', function () {
fetch('/updown_on', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ action: 'updown_on' }),
})
.then((response) => {
if (!response.ok) {
throw new Error('Failed to send updown on');
}
return response.text();
})
.then((data) => {
console.log(data);
logAction('FE : updown_on api sent successfully - progress.js');
})
.catch((error) => console.error('Error sending updown on:', error));
});
upDownOffButton.addEventListener('click', function () {
fetch('/updown_off', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ action: 'updown_off' }),
})
.then((response) => {
if (!response.ok) {
throw new Error('Failed to send updown off');
}
return response.text();
})
.then((data) => {
console.log(data);
logAction('FE : updown_off api sent successfully - progress.js');
})
.catch((error) => console.error('Error sending updown off:', error));
});
leftRightOnButton.addEventListener('click', function () {
fetch('/leftright_on', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ action: 'leftright_on' }),
})
.then((response) => {
if (!response.ok) {
throw new Error('Failed to send leftright on');
}
return response.text();
})
.then((data) => {
console.log(data);
logAction('FE : leftright_on api sent successfully - progress.js');
})
.catch((error) => console.error('Error sending leftright on:', error));
});
leftRightOffButton.addEventListener('click', function () {
fetch('/leftright_off', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ action: 'leftright_off' }),
})
.then((response) => {
if (!response.ok) {
throw new Error('Failed to send leftright off');
}
return response.text();
})
.then((data) => {
console.log(data);
logAction('FE : leftright_off api sent successfully - progress.js');
})
.catch((error) => console.error('Error sending leftright off:', error));
});
});
+5
View File
@@ -0,0 +1,5 @@
{
"currentDataIndex": 0,
"currentLayerIndex": 0,
"currentLayerHeight": 0
}
+3
View File
@@ -0,0 +1,3 @@
{
"measuredHeight": 0
}
+22
View File
@@ -0,0 +1,22 @@
{
"grid1": {
"currentDataIndex": 0,
"currentLayerIndex": 0,
"currentLayerHeight": 0
},
"grid2": {
"currentDataIndex": 1,
"currentLayerIndex": 0,
"currentLayerHeight": 0
},
"grid3": {
"currentDataIndex": 2,
"currentLayerIndex": 0,
"currentLayerHeight": 0
},
"grid4": {
"currentDataIndex": 3,
"currentLayerIndex": 0,
"currentLayerHeight": 0
}
}
+86
View File
@@ -0,0 +1,86 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>레이어 관리</title>
<link rel="stylesheet" href="../static/css/layer.css" />
</head>
<body>
<div id="alert">
<div id="alertBox">
<div id="loader"></div>
<span id="alert_msg"></span>
</div>
</div>
<div class="container">
<main>
<div class="leftContainer">
<div class="leftbuttonBox">
<button id="normalButton">일반 쌓기</button>
<button id="oneLineButton">한줄 쌓기</button>
<div class="inputContainer">
<div id="palletHeightTitle" style="display: none">
납작 팔레트 높이
</div>
<input
type="number"
id="palletHeight"
placeholder="mm"
style="display: none"
/>
<div id="palletHeightTitle" style="display: none">
팔레트 높이
</div>
<input
type="number"
id="palletHeight"
placeholder="mm"
style="display: none"
/>
</div>
</div>
</div>
<div class="middleContainer">
<div class="middleBoxContainer">
<div class="buttonBox">
<div class="buttonWithImage">
<img
src="../static/img/wooden_wheel_pallet.png"
alt="나무 바퀴 팔레트"
/>
<button id="woodenWheelPallet">나무 바퀴</button>
</div>
<div class="buttonWithImage">
<img
src="../static/img/iron_wheel_pallet.png"
alt="철제 바퀴 팔레트"
/>
<button id="ironWheelPallet">철제 바퀴</button>
</div>
<div class="buttonWithImage">
<img
src="../static/img/wooden_flat_pallet.png"
alt="나무 납작 팔레트"
/>
<button id="woodenFlatPallet">나무 납작</button>
</div>
</div>
</div>
</div>
<div class="rightContainer">
<button id="exitAppButton">앱 종료</button>
<button id="resetButton">데이터 초기화</button>
<div></div>
<button id="sendPaletteInfo">데이터 저장</button>
<button id="startRobotButton" disabled>로봇 동작 시작</button>
</div>
</main>
</div>
<script src="../static/js/layer.js"></script>
</body>
</html>
+55
View File
@@ -0,0 +1,55 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>로봇 동작 중</title>
<link rel="stylesheet" href="../static/css/oneLineProgress.css" />
</head>
<body>
<div class="container">
<main>
<div class="leftContainer">
<div class="gridContainer">
<div class="gridItem" id="grid2">2</div>
<div class="gridItem" id="grid4">4</div>
<div class="gridItem" id="grid1">1</div>
<div class="gridItem" id="grid3">3</div>
</div>
<div class="buttonContainer">
<div id="currentLayerIndexDisplay">현재 쌓고 있는 단 :</div>
<button id="run">한줄 쌓기 실행</button>
<button id="stop">한줄 쌓기 멈춤</button>
</div>
</div>
<div class="rightContainer">
<div class="buttonBox">
<button id="exitAppButton">앱 종료</button>
</div>
<div class="buttonBox">
<button id="stackerStartButton">스태커 시작</button>
<button id="stackerStopButton">스태커 정지</button>
</div>
<div class="buttonBox">
<button id="upDownOnButton">올림</button>
<button id="upDownOffButton">내림</button>
</div>
<div class="buttonBox">
<button id="leftRightOnButton">닫힘</button>
<button id="leftRightOffButton">열림</button>
</div>
<div class="inputContainer">
<button id="finishAction">바로 책 집어가기</button>
<button id="terminateAction">로봇 동작 종료</button>
</div>
</div>
</main>
</div>
</body>
<script src="../static/js/oneLineProgress.js"></script>
</html>
+51
View File
@@ -0,0 +1,51 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>로봇 동작 중</title>
<link rel="stylesheet" href="../static/css/progress.css" />
</head>
<body>
<div class="container">
<main>
<div class="leftContainer">
<div class="msg">로봇이 움직이는 중입니다.</div>
<div id="indexDisplay">
<p>
현재 <span id="currentLayerIndex">0</span>단
<span id="currentDataIndex">0</span>위치
</p>
<!-- <P>책 높이 : <span id="currentSensorHeight">0</span></P> -->
</div>
<button id="finishAction">바로 책 집어가기</button>
<button id="terminateAction">로봇 동작 종료</button>
</div>
<div class="rightContainer">
<div class="buttonBox">
<button id="exitAppButton">앱 종료</button>
</div>
<div class="buttonBox">
<button id="stackerStartButton">스태커 시작</button>
<button id="stackerStopButton">스태커 정지</button>
</div>
<div class="buttonBox">
<button id="upDownOnButton">올림</button>
<button id="upDownOffButton">내림</button>
</div>
<div class="buttonBox">
<button id="leftRightOnButton">닫힘</button>
<button id="leftRightOffButton">열림</button>
</div>
</div>
</main>
</div>
</body>
<script src="../static/js/progress.js"></script>
</html>