Commit b91ed10c by SHINDAESUB

AOI 작업자 업데이트

parent 4208ef77
TEACHING_RESOLUTION=(3840x2160)
TEACHING_IMAGE_PATH=/home/falinux/sds/backend/public/image/capture.jpg
WPO=1003_AOI01
\ No newline at end of file
WPO=1003_AOI01
PORT=3000
\ No newline at end of file
TEACHING_RESOLUTION=(3840x2160)
TEACHING_IMAGE_PATH=/home/falinux/sds/backend/public/image/capture.jpg
WPO=1003_AOI01
\ No newline at end of file
WPO=1003_AOI01
PORT=3000
\ No newline at end of file
const express = require('express')
const http = require('http')
const path = require('path')
const fs = require('fs');
const dgram = require('dgram');
const express = require("express")
const history = require("connect-history-api-fallback")
const path = require("path")
const apiRouter = require("./routes/index")
const bodyParser = require('body-parser');
const app = express();
const dotenv = require('dotenv');
const app = express()
const bodyParser = require('body-parser'); //
app.use(express.json({ limit : "50mb" }));
app.use(express.urlencoded({ limit:"50mb", extended: false }));
dotenv.config();
dotenv.config({
path: path.resolve(
process.cwd(),
process.env.NODE_ENV == "production" ? ".env" : ".env.dev"
)
});
const publicPath = path.resolve(__dirname, "public")
const server = dgram.createSocket('udp4');
// socket 실행
server.bind(9400);
app.use(express.json({ limit : "50mb" }))
app.use(express.urlencoded({ limit:"50mb", extended: false }))
server.on('listening', function() {
console.log('Socket Port : 9400');
});
server.on('message', function(msg ) {
udpResultMsg = msg.toString();
console.log("케켘", udpResultMsg)
});
server.on('close', function() {
console.log('Server UDP Socket close');
});
const client = dgram.createSocket('udp4');
const clientPort= 9300;
// const clientPort= 4403;
// const clientHost = '192.168.52.122'; //inet
const clientHost ='192.168.52.38';
// Error [ERR_SOCKET_DGRAM_NOT_RUNNING]: Not running
client.on('close', function() {
console.log('Client UDP socket close')
});
// const jsonFile = fs.readFileSync('./json/projects.json', 'utf8');
// console.log(jsonFile);
// const jsonData = JSON.parse(jsonFile);
// console.table(jsonData);
//Post 방식은 Get 과 다르기 때문에 body-parser 를 설치해서 사용해야한다.
//post body 값을 가져오기 위함
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}))
//vue router 연동하기 위한 설정
app.use(require('connect-history-api-fallback')());
app.use(express.static(publicPath))
app.use(express.static(path.join(__dirname, 'public')));
app.use("/api", apiRouter); // '/api' 로 시작하는 URL은 라우터로 전송
app.set('port',process.env.PORT || 3001);
app.use(history) //REST API 서버로서 사용한다면 라우터 뒤에 실행하게 한다. 그렇치 않으면 요청이 들어오지 않는다.
/* udp 통신으로 매니저에게 받는 결과값*/
let udpResultMsg = ''
/* udp 통신 기다리는 시간*/
let udpAwaitTime = 0
let newProjectNum = 0
app.get('/api/getWpoId',(req,res) => {
let id = process.env.WPO
res.send(id)
app.listen(process.env.PORT, () => {
console.log(`Neuromorphic web server start ${process.env.PORT}`)
})
app.post('/api/projectInfo',async (req,res) => {
const dataBuffer = fs.readFileSync('./json/project.json')
const project = JSON.parse(dataBuffer.toString())
console.log('로컬 프로젝트 :' , project)
// 받아온 값이 있을경우
if(Object.keys(req.body).length !== 0 ){
project.admin = req.body.admin
project.aoiUid = req.body.aoiUid
project.counter = req.body.counter
project.createDate = req.body.createDate
project.name = req.body.name
project.state = req.body.state
project.successDate = req.body.successDate
project.uid = req.body.uid
project.user = req.body.user
project.infos = req.body.infos
project.updateDate = new Date()
fs.writeFileSync('./json/project.json',JSON.stringify(project) , (err) =>{
if ( err ) return err;
})
}
res.status(200).json(project)
})
const wrapper = asyncFn => { return (async (req, res, next) => { try { return await asyncFn(req, res, next); } catch (error) { return next(error); } }); };
//https://kjwsx23.tistory.com/199 express 와 async ,await 이슈
//express async await wrapper 함수를 써서 감싸거나 라이브러리를 써야 한다.
app.get('/api/requestManager',wrapper(async (req,res)=>{
req.query.image_path = process.env.TEACHING_IMAGE_PATH
if(req.query.cmd === "neuro_check"){
req.query.teaching = `${process.env.TEACHING_IMAGE_PATH}-${req.query.project_num}-2-${process.env.TEACHING_RESOLUTION}${req.query.teaching_info}`
}
console.log("param :",JSON.stringify(req.query) )
sendToManager (JSON.stringify(req.query))
let requestMsg = ''
switch(req.query.cmd){
case "neuro_start":
requestMsg = 'neuro_start_done'
break
case "neuro_ready":
requestMsg = 'neuro_ready_done'
break
case "neuro_capture":
requestMsg = 'capture_done'
break
case "neuro_check" :
requestMsg = 'neuro_result'
break
// case "neuro_replay" :
// requestMsg = 'neuro_result'
// break
default:
res.status(404)
}
await responseManager(requestMsg)
.then(data => {
res.status(200).json(data)
})
.catch(error =>{
res.status(404).send(error);
})
}))
//UDP 메세지 보냄 통신
function sendToManager(msg){
client.send(msg, 0, msg.length, clientPort, clientHost, function(err) {
if ( err ) return ;
console.log("UDP send Msg ")
});
}
// await Promise 응답해야됨
function responseManager (requestMsg) {
return new Promise((resolve, reject) => {
let delay = 100; //ms 단위로 체크
let waitingTime = 0 //대기 시간
let timer = setInterval(() => {
if(requestMsg === JSON.parse(udpResultMsg).cmd ){
if(requestMsg === 'capture_done'){
let base64 = fs.readFileSync('./public/image/capture.jpg' , 'base64')
let msg = JSON.parse(udpResultMsg)
msg.imageBase64 = base64
udpResultMsg = JSON.stringify(msg)
// console.log("야이 씨벌 :", JSON.parse(udpResultMsg))
// console.log("base64 :",base64)
}
resolve(udpResultMsg)
clearInterval(timer)
}else if(waitingTime === 500000){
reject('No response from Manager')
clearInterval(timer)
}else{
waitingTime = waitingTime + delay
}
},delay)
});
}
http.createServer(app).listen(app.get('port'),function(){
console.log('WebServer Port: ' +app.get('port'))
})
This source diff could not be displayed because it is too large. You can view the blob instead.
[{"project_num_seq":4}]
\ No newline at end of file
......@@ -5,7 +5,7 @@
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node app.js"
"start": "nodemon app.js"
},
"author": "",
"license": "ISC",
......@@ -13,6 +13,7 @@
"body-parser": "^1.19.0",
"connect-history-api-fallback": "^1.6.0",
"dotenv": "^10.0.0",
"express": "^4.17.1"
"express": "^4.17.1",
"nodemon": "^2.0.12"
}
}
const express = require("express")
const mangerRoutes = require('./manager')
const projectRoutes = require('./project')
const wpoRoutes = require('./wpo')
const router = express.Router()
router.use('/manager', mangerRoutes)
router.use('/project', projectRoutes)
router.use('/wpo', wpoRoutes)
module.exports = router;
\ No newline at end of file
const express = require('express')
const fs = require('fs');
const router = express.Router()
const dgram = require('dgram');
let udpResultMsg = ''
// socket 실행
const server = dgram.createSocket('udp4');
server.bind(9400);
server.on('listening',() => {
console.log('Socket Port : 9400');
});
server.on('message', (msg) => {
udpResultMsg = msg.toString();
console.log('받은 메시지 :',udpResultMsg)
});
server.on('close',() => {
console.log('Server UDP Socket close');
});
const manager = dgram.createSocket('udp4');
const managerPort= 9300;
const managerHost ='127.0.0.1';
const requestToManager = ((msg) => {
manager.send(msg, 0, msg.length, managerPort, managerHost, (err) => {
if ( err ) return ;
console.log("UDP send Msg : ",msg)
});
})
router.get('/', async (req,res)=>{
req.query.image_path = process.env.TEACHING_IMAGE_PATH
if(req.query.cmd === "neuro_check"){
req.query.teaching = `${process.env.TEACHING_IMAGE_PATH}-${req.query.project_num}-2-${process.env.TEACHING_RESOLUTION}${req.query.teaching_info}`
}
requestToManager(JSON.stringify(req.query))
let requestMsg = ''
switch(req.query.cmd){
case "neuro_start":
requestMsg = 'neuro_start_done'
break
case "neuro_ready":
requestMsg = 'neuro_ready_done'
break
case "neuro_capture":
requestMsg = 'capture_done'
break
case "neuro_check" :
requestMsg = 'neuro_result'
break
case "neuro_replay" :
requestMsg = 'btnpush'
break
case "network_check":
requestMsg = 'network_status'
break
case "neuro_complete":
requestMsg = 'complete_done'
break
default:
res.status(404)
}
await managerResponse(requestMsg)
.then(data => {
res.status(200).json(data)
})
.catch(error =>{
res.status(404).send(error);
})
})
const managerResponse = ((requestMsg) => {
return new Promise((resolve, reject) => {
let delay = 100; //ms 단위로 체크
let waitingTime = 0 //대기 시간
let timer = setInterval(() => {
if(udpResultMsg.length > 0 && requestMsg === JSON.parse(udpResultMsg).cmd){
if(requestMsg === 'capture_done'){
let base64 = fs.readFileSync('./public/image/capture.jpg' , 'base64')
let msg = JSON.parse(udpResultMsg)
msg.imageBase64 = base64
udpResultMsg = JSON.stringify(msg)
}
resolve(udpResultMsg)
clearInterval(timer)
}else if(waitingTime === 3_000_000){
reject('No response from Manager')
clearInterval(timer)
}else{
waitingTime = waitingTime + delay
}
},delay)
});
})
manager.on('close',() => {
console.log('Client UDP socket close')
});
module.exports = router
\ No newline at end of file
const express = require('express')
const fs = require('fs');
const router = express.Router()
router.get('/',async (req,res) => {
const project = JSON.parse( fs.readFileSync('./json/project.json').toString() )
res.send(project)
})
router.post('/',async (req,res) => {
const project = JSON.parse( fs.readFileSync('./json/project.json').toString() )
// 받아온 값이 있을경우
if(Object.keys(req.body).length !== 0 ){
project.admin = req.body.admin
project.aoiUid = req.body.aoiUid
project.counter = req.body.counter
project.createDate = req.body.createDate
project.name = req.body.name
project.state = req.body.state
project.successDate = req.body.successDate
project.uid = req.body.uid
project.user = req.body.user
project.infos = req.body.infos
project.updateDate = new Date()
project.total = req.body.total
project.success = req.body.success
project.fail = req.body.fail
project.result = req.body.result
fs.writeFileSync('./json/project.json',JSON.stringify(project) , (err) =>{
if ( err ) return err;
})
}else{
fs.writeFileSync('./json/project.json',JSON.stringify({}) , (err) =>{
if ( err ) return err;
})
}
res.status(200).send(project)
})
module.exports = router
\ No newline at end of file
const express = require('express')
const router = express.Router()
router.get('/',async ( req , res) => {
let id = await process.env.WPO
res.send(id)
})
module.exports = router
\ No newline at end of file
......@@ -24,7 +24,8 @@
"vue-konva": "^2.1.6",
"vue-router": "^3.2.0",
"vuetify": "^2.2.11",
"vuex": "^3.4.0"
"vuex": "^3.4.0",
"vuex-persistedstate": "^4.1.0"
},
"devDependencies": {
"@mdi/js": "^5.5.55",
......
......@@ -6,6 +6,16 @@
:message="message"
:type="type"
/>
<Loading
:open="loadingModal"
:message="message"
/>
<Bluetooth
:open="bluetoothModal"
/>
<router-view/>
</v-main>
</v-app>
......@@ -15,12 +25,17 @@
import { EventBus } from '@/event-bus'
import Alert from './components/Alert.vue'
import Loading from './components/Loading.vue'
import Bluetooth from './components/Bluetooth.vue'
export default {
name: 'App',
components: {
Alert
Alert,
Loading,
Bluetooth,
},
created() {
......@@ -31,10 +46,23 @@ export default {
}),
EventBus.$on('closeAlert',() => { this.alertOpen = false })
EventBus.$on('openLoading',( massage ) => {
this.loadingModal = true
this.message = massage
}),
EventBus.$on('closeLoading',() => { this.loadingModal = false })
EventBus.$on('openBluetooth',() => { this.bluetoothModal = true }),
EventBus.$on('closeBluetooth',() => { this.bluetoothModal = false })
},
data: () => ({
bluetoothModal:false,
loadingModal:false,
alertOpen: false ,
message:'',
type:'',
......@@ -43,9 +71,12 @@ export default {
mounted(){
// this.$store.dispatch('getProjectList')
},
beforeDestroy(){
EventBus.$off('openAlert');
EventBus.$off('closeAlert');
EventBus.$off('openLoading');
EventBus.$off('closeLoading');
}
};
</script>
......
<template>
<v-overlay v-if="open" :z-index="10">
<v-card max-width="1500" min-width="1500" color="blue-grey lighten-5" light >
<v-toolbar
color="indigo "
class="elevation-0"
>
<v-toolbar-title class="white--text">블루투스 연결 (네트워크 연결)</v-toolbar-title>
</v-toolbar>
<v-row class='px-4'>
<v-col cols="6">
<v-subhaeder>안드로이드</v-subhaeder>
<v-card>
<v-row no-gutters>
<v-col cols="6">
<v-img
src="@/../public/image/android.jpg"
height="525"
contain
></v-img>
</v-col>
<v-col cols="6" class="d-flex justify-center align-center flex-column ">
<p class="text-h5">안드로이드 연결</p>
<p>1. 블루투스를 컨다</p>
<p>2. 블루투스 목록중 <strong>'falinux'</strong> 찾는다</p>
<p>3. <strong>'falinux'</strong> 선택</p>
<p>4. 모바일에서 핫스팟을 컨다</p>
</v-col>
</v-row>
</v-card>
</v-col>
<v-col cols="6">
<v-subhaeder>아이폰</v-subhaeder>
<v-card color="">
<v-row no-gutters>
<v-col cols="6">
<v-img
src="@/../public/image/ipone.jpg"
height="525"
contain
></v-img>
</v-col>
<v-col cols="6" class="d-flex justify-center align-center flex-column ">
<p class="text-h5">아이폰 연결</p>
<p>1. 블루투스를 컨다</p>
<p>2. 블루투스 목록중 <strong>'falinux'</strong> 찾는다</p>
<p>3. <strong>'falinux'</strong> 선택</p>
<p>4. 모바일에서 핫스팟을 컨다</p>
</v-col>
</v-row>
</v-card>
</v-col>
</v-row>
</v-card>
</v-overlay>
</template>
<script>
export default {
props:{
open:Boolean,
},
}
</script>
<style lang="scss">
</style>
<template>
<v-overlay
class="loading-overlay"
:z-index="20"
:value="open">
<v-progress-circular
indeterminate
:size="150"
:width="7"
class="title"
>{{message}}...</v-progress-circular>
</v-overlay>
</template>
<script>
export default {
props: {
open:Boolean,
message:String,
},
}
</script>
import gql from 'graphql-tag' // eslint-disable-line no-unused-vars
import apolloProvider from '../plugins/apollo'
export default {
async conncetion(input){
try{
const response = await apolloProvider.defaultClient.query({
query: gql`
query neuroAoiSerial($serial: String!) {
neuroAoiSerial(serial: $serial) {
uid
projectUid
name
state
serial
etc
updateDate
createDate
}
}`,
fetchPolicy: 'no-cache',
variables: { serial: input }
})
let aoi = response.data.neuroAoiSerial
console.log('aoi :',aoi)
return aoi
}catch (e) {
return false
}
},
async getAois(){
try{
const response = await apolloProvider.defaultClient.query({
query: gql`
{
neuroAois{
uid
name
state
serial
etc
projectUid
createDate
updateDate
}
}
`,
fetchPolicy: 'no-cache'
})
console.log('gql 에서 생성하는 :', response.data.neuroAois)
let aois = response.data.neuroAois
if (!aois) return ''
if (aois.errors) throw new Error(aois.errors[0])
return aois
}catch (e) {
console.log(e)
throw e
}
},
async updateAoi(input){
try{
const response = await apolloProvider.defaultClient.mutate({
mutation: gql`
mutation updateNeuroAoi( $name: String! ,$state: Boolean! ,$createDate: String! , $serial:String! , $etc:String! ,$updateDate:String! ,$uid:Int! , $projectUid:Int ){
updateNeuroAoi(input:{
name:$name
state:$state
createDate:$createDate
serial:$serial
etc:$etc
updateDate:$updateDate
projectUid:$projectUid
uid:$uid
})
{
neuroAoi{
uid
name
state
serial
etc
createDate
updateDate
projectUid
}
}
}
`,
variables: {
uid:input.uid,
name:input.name,
state:input.state,
serial:input.serial,
etc:input.etc,
createDate:input.createDate,
updateDate:new Date(),
projectUid:0
}
})
let aoi = response.data.updateNeuroAoi.neuroAoi
if (!aoi) return ''
if (aoi.errors){
throw new Error(aoi.errors[0])
}
return aoi
}catch (e) {
console.log(e)
throw e
}
},
}
\ No newline at end of file
import gql from 'graphql-tag' // eslint-disable-line no-unused-vars
import apolloProvider from '../plugins/apollo'
export default {
async getLearning(projectUid){
console.log(projectUid)
try{
const response = await apolloProvider.defaultClient.query({
query: gql`
query neuroInfo($projectUid: Int!) {
neuroInfo(projectUid: $projectUid) {
projectUid
order
type
startX
startY
lastX
lastY
width
height
goodImage
missingImage
goodPath
missingPath
}
}`,
variables: { projectUid: projectUid }
})
let learning = response.data.neuroInfo
if (!learning) return ''
if (learning.errors) throw new Error(learning.errors[0])
return learning
}catch (e) {
console.log(e)
throw e
}
},
}
\ No newline at end of file
import gql from 'graphql-tag' // eslint-disable-line no-unused-vars
import apolloProvider from '../plugins/apollo'
export default {
async get(uid){
try{
const response = await apolloProvider.defaultClient.query({
query: gql`
query neuroProject($aoiUid:Int!) {
neuroProject(aoiUid:$aoiUid) {
name
admin
user
createDate
successDate
counter
aoiUid
uid
state
updateDate
}
}`,
fetchPolicy: 'no-cache',
variables: { aoiUid: uid }
})
let project = response.data.neuroProject
if (!project) return ''
if (project.errors) throw new Error(project.errors[0])
return project
}catch (e) {
return false
}
},
async update(input){
try{
const response = await apolloProvider.defaultClient.mutate({
mutation: gql`
mutation updateNeuroProject(
$name: String! ,
$admin: String! ,
$user: String! ,
$createDate: String! ,
$successDate: String! ,
$counter:Int ,
$aoiUid:Int! ,
$uid:Int!,
$state:Boolean!,
$updateDate:String,
$success:Int,
$fail:Int,
$total:Int,
$result:Boolean
){
updateNeuroProject(input:{
name:$name
admin:$admin
user:$user
createDate:$createDate
successDate:$successDate
counter:$counter
aoiUid:$aoiUid
uid:$uid
state:$state
updateDate:$updateDate,
success:$success,
fail:$fail,
total:$total,
result:$result
})
{
neuroProject{
name
admin
user
createDate
successDate
counter
aoiUid
uid
state
updateDate
success
fail
total
result
}
}
}
`,
variables: {
name: input.name,
admin: input.admin,
user: input.user,
createDate: input.createDate,
successDate: input.successDate,
counter: input.counter,
aoiUid: 0,
uid: input.uid,
state: input.state,
updateDate: new Date(),
success:input.success,
fail:input.fail,
total:input.total,
result:input.result,
}
})
let project = response.data.updateNeuroProject.neuroProject
if (!project) return ''
if (project.errors){
throw new Error(project.errors[0])
}
return true
}catch (e) {
console.log(e)
throw e
}
},
}
\ No newline at end of file
......@@ -6,8 +6,7 @@ import store from './store'
import vuetify from './plugins/vuetify';
import axios from 'axios'
import VueKonva from 'vue-konva'
import VueApollo from 'vue-apollo'
import ApolloClient from 'apollo-boost'
import apolloProvider from './plugins/apollo'
Vue.use(VueKonva)
......@@ -23,20 +22,7 @@ axios.interceptors.response.use( //Error 전파하기 위해 인터셉터 응
return Promise.reject(error)
}
)
const apolloClient = new ApolloClient({
uri: 'https://www.nemopai.com/graphql',
})
const apolloProvider = new VueApollo({
defaultClient: apolloClient,
})
Vue.use(VueApollo)
//Vue.prototype.$http = axios //vue 컴포넌트에서 this.$http 요청할 수 있게 된다.
Vue.config.productionTip = false
......
import Vue from 'vue'
import { ApolloClient } from 'apollo-client'
import { createHttpLink } from 'apollo-link-http'
import { InMemoryCache } from 'apollo-cache-inmemory'
import VueApollo from 'vue-apollo'
//멑티 클라이언트 구성
//https://apollo.vuejs.org/guide/multiple-clients.html
Vue.use(VueApollo)
const httpLink = createHttpLink({
uri: 'https://www.nemopai.com/graphql',
})
const link = httpLink
const apolloClient = new ApolloClient({
// Tells Apollo to use the link chain with the http link we set up.
link,
// Handles caching of results and mutations.
cache: new InMemoryCache({ freezeResults: false }),
// Useful if you have the Apollo DevTools installed in your browser.
connectToDevTools: true
});
const apolloProvider = new VueApollo({
defaultClient: apolloClient
});
export default apolloProvider;
......@@ -3,7 +3,7 @@ import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
Vue.use(VueRouter)
const routes = [
const routes = [
{
path: '/',
name: 'home',
......
......@@ -32,7 +32,6 @@ export default {
// axios.get('/neuroCapture',{ params: param })
projectUpdate (data) {
console.log("파라미터 : " ,data)
return new Promise((resolve,reject) =>{
// axios.post('/projectModify',{ params: data })
axios.post('/projectUpdate',data)
......@@ -45,7 +44,6 @@ export default {
},
projectRemove (data) {
console.log("파라미터 : " ,data)
return new Promise((resolve,reject) =>{
// axios.post('/projectModify',{ params: data })
axios.post('/projectUpdate',data)
......
......@@ -2,10 +2,10 @@ import axios from 'axios'
/* {data} 받으면 data.data. => data. 접근 가능 */
export default{
async requestManager (param) {
async request(param) {
try{
let repsone = await axios.get('/requestManager',{ params: param })
return repsone
let response = await axios.get('/manager',{ params: param })
return response
}catch(e){
console.error(e.message)
}
......
......@@ -3,28 +3,25 @@ export default{
neuroStart (param){
return new Promise((resolve,reject) => {
console.log("param :", param)
console.log("param.cmd :",JSON.parse(param).cmd)
JSON.parse(param).cmd === "neuro_start" ? resolve(param) : reject(new Error("요청 값이 틀립니다."))
})
},
neuroCapture (param){
return new Promise((resolve,reject) => {
console.log("param :", param)
console.log("param.cmd :",JSON.parse(param).cmd)
JSON.parse(testJson).cmd === "capture_done" ? resolve(testJson) : reject(new Error("요청 값이 틀립니다."))
})
},
neuroCheck (param){
return new Promise((resolve,reject) => {
console.log("param :", param)
console.log("JSON.parse(param).cmd :",JSON.parse(param).cmd)
JSON.parse(param).cmd === "neuro_check" ? resolve("검수 성공 ") : reject(new Error("요청 값이 틀립니다."))
})
},
neuroSave (param){
return new Promise((resolve,reject) => {
console.log("param :", param)
JSON.parse(testJson).cmd === "capture_done" ? resolve(testJson) : reject(new Error("요청 값이 틀립니다."))
})
},
......
import axios from 'axios'
export default {
async projectInfo (project , infos ) {
async get() {
try{
const response = await axios.get('/project')
return response.data
}catch (e) {
console.log(e)
throw e
}
},
async save(project , infos ) {
if(Object.keys(project).length !== 0) project.infos = infos
try{
let repsone = await axios.post('/projectInfo',project)
let repsone = await axios.post('/project',project)
return repsone
}catch(e){
console.error(e.message)
......
......@@ -3,8 +3,8 @@ import axios from 'axios'
export default {
async getWpoId () {
try{
let repsone = await axios.get('/getWpoId')
return repsone
let response = await axios.get('/wpo')
return response.data
}catch(e){
console.error(e.message)
}
......
// import projectService from '@/service/project'
// export const getProjectList = ({commit}) => {
// projectService.getProjectList()
// .then(data => {
// commit('projectList',data)
// })
// }
export const getProjectList = ({commit} , projects) => {
console.log(`호출`)
commit('projectList',projects)
}
export const modifyPoject = ({commit}, info) => {
commit('modifyPoject', info)
}
export const projectUpdate = ({commit}, updateData) => {
commit('projectUpdate', updateData)
}
export const projectAdd = ({commit}, newData) => {
commit('projectAdd', newData)
}
export const projects = state => state.projects
// export const projectListS = state => {
// return state.projectList.filter(s => s.project_state === "success")
// }
// export const projectListW = state => {
// return state.projectList.filter(w => w.project_state === "waiting")
// }
// export const projectListP = state => {
// return state.projectList.filter(p => p.project_state === "proceeding")
// }
// export const projectListC = state => {
// return state.projectList.filter(c => c.project_state === "")
// }
import Vue from 'vue'
import Vuex from 'vuex'
import * as getters from './getters'
import * as actions from './actions'
import mutations from './mutations'
import createLogger from 'vuex/dist/logger'
/* state 값을 사용할 떄는 부모 컴포넌트, 공통 컴포넌트 에서 관리해야한다.*/
// import projectStore from './modules/projectStore.js'
import aoiStore from './modules/aoiStore'
import createPersistedState from 'vuex-persistedstate';
Vue.use(Vuex)
const state = {
projects : [],
}
export default new Vuex.Store({
state,
getters,
mutations,
actions,
plugins: process.env.NODE_ENV !== 'production'
? [createLogger()]
: []
})
\ No newline at end of file
modules:{
// project:projectStore,
aoi:aoiStore
},
plugins:[
createPersistedState({
paths:['aoi']
}),
]
})
import aoiGQL from '../../gql/aoi'
const state = {
aois: [],
}
const getters = {
}
const actions = {
async getAois({commit}){
let result = await aoiGQL.getAois()
commit('GETAOIS' , result)
},
async createAoi({commit} , input){
let result = await aoiGQL.createAoi(input)
commit('CREATEPAOI' , result)
},
async updateAoi({commit} , input){
let result = await aoiGQL.updateAoi(input)
commit('UPDATEAOI' , result)
},
async deleteAoi({commit} , uid){
let result = await aoiGQL.deleteAoi(uid)
commit('DELETEAOI' , result)
}
}
const mutations = {
GETAOIS(state,payload ){
state.aois = payload
},
CREATEPAOI(state,payload ){
state.aois.push(payload)
},
UPDATEAOI(state,payload){
let updateIndex =state.aois.findIndex( aoi => aoi.uid === payload.uid )
state.aois.splice(updateIndex , 1 ,payload)
},
DELETEAOI(state,payload ){
let removeIndex = state.aois.findIndex( aoi => aoi.uid === payload )
state.aois.splice(removeIndex , 1 )
}
}
export default {
state: {
...state
},
getters,
mutations,
actions
}
\ No newline at end of file
export default {
projectList (state , data){
state.projects = data
},
modifyPoject(state , payload){
for(let i in state.projectList){
if(state.projectList[i].project_num === payload.project_num){
state.projectList[i].current_done = payload.current_done
state.projectList[i].current_reject = payload.current_reject
state.projectList[i].current_counter = payload.current_counter
// state.projectList[i].total_num = payload.total_num
}
}
},
projectUpdate(state, payload){
for(let i in state.projectList){
if(state.projectList[i].project_num === payload.project_num){
if(payload.msg === "update"){
state.projectList[i].project_admin = payload.project_admin
state.projectList[i].project_user = payload.project_user
state.projectList[i].total_num = payload.total_num
state.projectList[i].start_date = payload.start_date
state.projectList[i].end_date = payload.end_date
}else{
state.projectList[i].project_state = "remove"
}
}
}
},
projectAdd(state,payload){
state.projectList.push(payload)
}
}
\ No newline at end of file
<template>
<v-card
:disabled="this.step <= 4"
width="960"
max-width="960"
width="1280"
max-width="1280"
height="846"
max-height="846"
dark
class="elevation-0 mx-auto"
>
<v-img
<v-card
v-if="captureImage === null"
height="540"
max-height="540"
height="720"
max-height="720"
width="100%"
max-width="100%"
src="../../../public/image/lazy.png"
lazy-src="../../../public/image/lazy.png"
contain
class="grey darken-4"
max-width="100%"
class="elevation-0 d-flex justify-center align-center flex-column"
>
</v-img>
<v-row
class="fill-height"
align-content="center"
justify="center"
>
<v-col
class="text-h3 text-center"
cols="12"
>
<span><strong class="white--text success">보드</strong>를 삽입해주세요</span>
</v-col>
<v-col cols="12">
<v-progress-linear
color="blue-grey"
indeterminate
rounded
height="6"
></v-progress-linear>
</v-col>
</v-row>
</v-card>
<v-stage
v-else
class="ma-0 pa-0 "
......@@ -32,64 +49,44 @@
<v-image
:config="{
image: image,
width: 960,
height: 540,
width: 1280,
height: 720,
}"/>
<v-rect
v-for="(rec, index) in rect"
:key="index"
:config="{
x: Math.min(rec.startX /1.33, rec.lastX /1.33 ),
y: Math.min(rec.startY /1.33, rec.lastY /1.33),
width: Math.abs(rec.width / 1.33 ),
height: Math.abs(rec.height /1.33),
x: Math.min(rec.startX , rec.lastX ),
y: Math.min(rec.startY , rec.lastY),
width: Math.abs(rec.width ),
height: Math.abs(rec.height ),
fill: rec.stroke+'4D',
stroke: rec.stroke,
dash:[8,8],
strokeWidth: 3,
}"
>
<!-- height: Math.abs(rec.height /1.33),
fill: '#2196F34D',
stroke: '#2196F3', -->
</v-rect>
</v-layer>
</v-stage>
<div v-if="Object.keys(project).length !== 0">
<v-card-title>{{project.name}}</v-card-title>
<v-card-subtitle>{{project.createDate}} - {{project.successDate}}</v-card-subtitle>
<v-card-text class="text--primary">
<div>검수 : {{project.counter}}</div>
<div>관리자 : {{project.admin}}</div>
<div>사용자 : {{project.user}}</div>
</v-card-text>
<v-divider class="mx-4"></v-divider>
<v-card-title>검수 진행률 </v-card-title>
<v-card-subtitle>{{counter}}/{{project.counter}} </v-card-subtitle>
<v-card-text>
<v-progress-linear
v-model="progressbar"
height="25"
dark
reactive
@click.prevent.self
>
<strong>{{ progressbar }} %</strong>
</v-progress-linear>
</v-card-text>
</div>
</v-stage>
<v-card-text>
<v-progress-linear
class="mt-10"
v-if="captureImage !== null"
color="green darken-2"
v-model="progressbar"
height="40"
dark
reactive
@click.prevent.self
>
<strong>{{ progressbar }} %</strong>
</v-progress-linear>
</v-card-text>
</v-card>
</template>
<script>
// const test = require("../../../public/image/lazy.png");
export default {
......@@ -104,8 +101,8 @@ export default {
check:[],
stageSize: {
width: 960,
height: 540,
width: 1280,
height: 720,
},
drwingColor: '#2196F3', // 선 색
......@@ -113,10 +110,6 @@ export default {
}
},
created() {
},
props: {
step:Number,
wpo:String,
......@@ -138,34 +131,23 @@ export default {
this.rect = [...this.infos]
for(let i = 0; i < current.length; i++ ){
Number(current[i]) === 0 ? (this.rect[i].fill = '#2196F3' , this.rect[i].stroke = '#2196F3' ) : (this.rect[i].fill = '#F44336' , this.rect[i].stroke = '#F44336' )
Number(current[i]) === 0 ? (this.rect[i].fill = '#0D47A1' , this.rect[i].stroke = '#0D47A1' ) : (this.rect[i].fill = '#B71C1C' , this.rect[i].stroke = '#B71C1C' )
}
}
},
step(current){
if(current === 5){
let image = new Image();
image.src = 'data:image/jpeg;base64,'+this.captureImage
image.onload = () => { this.image = image};
// const image = new window.Image();
// image.src = require("../../../public/image/capture.jpg");
// // image.src = require("@/public/image/lazy.png");
// console.log("이미지:", image)
// image.onload = () => {
// // set image only when it is loaded
// this.image = image;
// };
}
if(current === 6){
this.rect =[]
}
},
captureImage(current){
let image = new Image();
image.src = 'data:image/jpeg;base64,'+current
image.onload = () => { this.image = image};
}
}
}
}
</script>
......
......@@ -3,7 +3,7 @@
dense
height="400"
class="elevation-0"
:disabled="step < 4"
>
<v-toolbar
class="elevation-0 "
......@@ -19,17 +19,6 @@
:height="220"
:percent="percent"
/>
<v-card-text>
<v-list-item>
<v-list-item-subtitle>응답 속도 합계</v-list-item-subtitle>
<v-list-item-title>{{total === 0 ? 0 +' sec' : total.toFixed(2) + ' sec'}}</v-list-item-title>
</v-list-item>
<v-list-item>
<v-list-item-subtitle>응답 속도 평균</v-list-item-subtitle>
<v-list-item-title>{{total/counter === 0 ? 0 +' sec' : (total/counter).toFixed(2) + ' sec'}} </v-list-item-title>
</v-list-item>
</v-card-text>
</div>
</v-card>
</template>
......@@ -75,7 +64,6 @@ export default {
watch:{
result(current){
if(current.length !== 0){
this.total = this.total + current[current.length-1]['time']
......@@ -93,6 +81,15 @@ export default {
],
}
}
},
step(current){
if(current === 1){
this.total = 0,
this.totalColl ={},
this.success = 0,
this.fail = 0,
this.percent = 0
}
}
}
......
<template>
<v-data-table
:disabled="loading"
height="422"
max-height="422"
height="300px"
min-height="300px"
max-height="300px"
width="100%"
max-width="100%"
max-width="100%"
dense
:headers="headers"
:items="result"
......@@ -12,11 +13,11 @@
no-data-text="검수 이력이 없습니다."
:sort-by="['counter']"
:sort-desc="[true]"
class="elevation-0 mt-6"
class="elevation-0 mt-6 overflow-x-hidden overflow-y-auto"
hide-default-footer
>
<template v-slot:[`item.counter`]="{ item }">
{{item.counter}}
<template v-slot:[`item.total`]="{ item }">
{{item.total}}
</template>
<template v-slot:[`item.state`]="{ item }">
<v-chip
......@@ -39,9 +40,8 @@ export default {
return {
loading: true,
headers: [
{ text: '횟수', align: 'start', sortable: false, value: 'counter',},
{ text: '횟수', align: 'start', sortable: false, value: 'total',},
{ text: '검수 결과', align: 'center', value: 'state' },
{ text: '응답 속도 (sec)',align: 'center', value: 'time' },
],
}
},
......
......@@ -4,9 +4,10 @@
v-model="step"
vertical
style="height:100%"
class="font-weight-black"
class="font-weight-black elevation-0 "
width="100%"
max-width="100%"
>
<v-toolbar
color="blue darken-1"
......@@ -15,10 +16,11 @@
>
<v-toolbar-title>{{wpo}}</v-toolbar-title>
<v-spacer></v-spacer>
<v-chip v-if="local" light color="yellow">LOCAL</v-chip>
</v-toolbar>
<v-stepper-step
:color="step > 1 ? 'success' : 'primary'"
:color="step > 1 ? 'primary' : 'blue lighten-3'"
:complete="step > 1"
step="1"
class="font-weight-black"
......@@ -31,20 +33,20 @@
<div class="text-center">
<v-progress-circular
:size="100"
color="primary"
color="blue lighten-3"
indeterminate
></v-progress-circular>
</div>
</v-stepper-content>
<v-stepper-step
:color="step > 2 ? 'success' : 'primary'"
:color="step > 2 ? 'primary' : 'blue lighten-3'"
:complete="step > 2"
step="2"
class="font-weight-black"
>
<small class="font-weight-bold">Step 2</small>
데이터 불러오기
<small class="font-weight-bold">Step 2 </small>
<span> 데이터 불러오기 </span>
</v-stepper-step>
<v-stepper-content step="2" >
......@@ -57,7 +59,7 @@
<v-list-item-action>
<v-checkbox
:input-value="Object.keys(project).length !== 0 ? true : false"
color="primary"
color="blue lighten-3"
></v-checkbox>
</v-list-item-action>
......@@ -70,7 +72,7 @@
<v-list-item-action>
<v-checkbox
:input-value="infos.length !== 0 ? true : false"
color="primary"
color="blue lighten-3"
></v-checkbox>
</v-list-item-action>
......@@ -83,7 +85,7 @@
<v-list-item-action>
<v-checkbox
:input-value="step === 2 ? true : false"
color="primary"
color="blue lighten-3"
></v-checkbox>
</v-list-item-action>
<v-list-item-content>
......@@ -92,17 +94,10 @@
</v-list-item>
</v-list-item-group>
</v-list>
<v-btn
color="primary"
@click="step = 3"
>
Continue
</v-btn>
</v-stepper-content>
<v-stepper-step
:color="step > 3 ? 'success' : 'primary'"
:color="step > 3 ? 'primary' : 'blue lighten-3'"
:complete="step > 3"
step="3"
>
......@@ -114,14 +109,14 @@
<div class="text-center">
<v-progress-circular
:size="100"
color="primary"
color="blue lighten-3"
indeterminate
></v-progress-circular>
</div>
</v-stepper-content>
<v-stepper-step
:color="step > 4 ? 'success' : 'primary'"
:color="step > 4 ? 'primary' : 'blue lighten-3'"
:complete="step > 4"
step="4"
>
......@@ -133,14 +128,14 @@
<div class="text-center">
<v-progress-circular
:size="100"
color="primary"
color="blue lighten-3"
indeterminate
></v-progress-circular>
</div>
</v-stepper-content>
<v-stepper-step
:color="step > 5 ? 'success' : 'primary'"
:color="step > 5 ? 'primary' : 'blue lighten-3'"
:complete="step > 5"
step="5"
>
......@@ -152,14 +147,14 @@
<div class="text-center">
<v-progress-circular
:size="100"
color="primary"
color="blue lighten-3"
indeterminate
></v-progress-circular>
</div>
</v-stepper-content>
<v-stepper-step
:color="step === 6 ? 'success' : 'primary'"
:color="step === 6 ? 'primary' : 'blue lighten-3'"
:complete="step === 6"
step="6"
>
......@@ -168,7 +163,17 @@
</v-stepper-step>
<v-stepper-content step="6">
<div class="text-center">
<v-btn
class="elevation-3 mt-5 "
x-large
fab
color="yellow"
>
<v-icon>mdi-replay</v-icon>
</v-btn>
<div class="mt-3">상단 버튼을 누르면 재검수</div>
</div>
</v-stepper-content>
</v-stepper>
</template>
......@@ -181,6 +186,7 @@ export default {
wpo:String,
infos:Array,
project:Object,
local:Boolean
},
}
......
<template>
<v-card
width="100%"
height="850px"
class="elevation-0 "
>
<v-card-title class="blue-grey white--text ">AOI</v-card-title>
<v-container>
<v-row>
<v-col cols="4">
<v-card
:class="step === 0 ?'elevation-12':'elevation-0'"
:color="step === 0 ? '' : 'grey lighten-2'"
class="d-flex justify-center align-center"
height="740"
>
<v-row
class="fill-height"
align-content="center"
justify="center"
>
<v-col
class="text-center"
cols="12"
>
<span v-if="step > 0" class="text-h4">블루투스 연결 <v-icon color="success" class="pb-4" x-large>mdi-check-bold</v-icon> </span>
<span v-else class="text-h4 "><strong>블루투스</strong>를 연결해주세요</span>
</v-col>
<v-col v-if="step === 0" cols="8">
<v-progress-linear
color="blue-grey"
indeterminate
rounded
height="8"
></v-progress-linear>
</v-col>
</v-row>
</v-card>
</v-col>
<v-col cols="4">
<v-card
:class="step === 1 ?'elevation-12':'elevation-0'"
:color="step === 1 ? '' : 'grey lighten-2'"
class="d-flex justify-center align-center "
height="740"
>
<v-row
class="fill-height"
align-content="center"
justify="center"
>
<v-col
class="text-center"
cols="12"
>
<span v-if="step > 1" class="text-h4">AOI 연결 <v-icon color="success" class="pb-4" x-large>mdi-check-bold</v-icon> </span>
<div v-else class="text-h4 "><strong>AOI 장비</strong>와 연결해주세요
<p class="text-h4 "><strong>S/N : {{id}}</strong></p></div>
</v-col>
<v-col v-if="step === 1" cols="8">
<v-progress-linear
color="blue-grey"
indeterminate
rounded
height="8"
></v-progress-linear>
</v-col>
</v-row>
</v-card>
</v-col>
<v-col cols="4">
<v-card
:class="step === 2 ?'elevation-12':'elevation-0'"
:color="step === 2 ? '' : 'grey lighten-2'"
class="d-flex justify-center align-center"
height="740"
>
<v-row
class="fill-height"
align-content="center"
justify="center"
>
<v-col
class="text-center"
cols="12"
>
<span v-if="step > 2" class="text-h4">프로젝트 연동 완료 <v-icon color="success" class="pb-4" x-large>mdi-check-bold</v-icon> </span>
<span v-else class="text-h4 "><strong>프로젝트</strong>를 생성해주세요</span>
</v-col>
<v-col v-if="step === 2" cols="8">
<v-progress-linear
color="blue-grey"
indeterminate
rounded
height="8"
></v-progress-linear>
</v-col>
</v-row>
</v-card>
</v-col>
</v-row>
</v-container>
</v-card>
</template>
<script>
import wpoService from '@/service/wpo'
import managerService from '@/service/manager'
import { mapActions } from 'vuex'
import { EventBus } from '@/event-bus'
export default {
data () {
return {
message: '준비중...',
step:0,
id:'',
}
},
created(){
this.getAoi()
this.network()
this.getAois()
},
methods: {
...mapActions([
'getAois',
]),
async getAoi(){
let id = await wpoService.getWpoId() //웹서버에 지정한 WPO ID 가져오기
this.id = id
},
async network(){
EventBus.$emit('openBluetooth')
try{
const msg = new Object()
msg.cmd = "network_check"
let response = await managerService.request(msg)
if(JSON.parse(response.data).status){
EventBus.$emit('closeBluetooth')
this.step = 1
}else{
this.network()
}
}catch(e){
// EventBus.$emit('openAlert',`${e.message}`, 'error')
}
},
aoi(){
let delay = 1000;
let timer = setInterval(()=> {
let test = this.$store.state.aoi.aois.filter( aoi => { return aoi.serial === this.id } )
console.log('test :',test)
console.log(' this.$store.state.aoi.aois :', this.$store.state.aoi.aois)
if(test.length !== 0){
clearInterval(timer)
this.step = 2
}else{
this.getAois()
}
},delay)
},
project(){
let delay = 1000;
let timer = setInterval(()=> {
let test = this.$store.state.aoi.aois.filter( aoi => { return aoi.serial === this.id } )
console.log('test :',test[0].projectUid)
if(test[0].projectUid !== 0){
clearInterval(timer)
this.step = 3
setTimeout(EventBus.$emit('setTeaching' , 2 , test[0].uid ), 3000);
}else{
this.getAois()
}
},delay)
}
},
watch:{
async step(current) {
switch(current) {
case 1:
this.aoi()
break;
case 2:
this.project()
break;
}
}
}
}
</script>
<style>
</style>
\ No newline at end of file
<template>
<v-card
width="100%"
height="850px"
class="elevation-0 "
>
<v-card-title class="blue-grey white--text ">PROJECT</v-card-title>
<v-container>
<v-row>
<v-col cols="4">
<v-card
:class="step === 0 ?'elevation-12':'elevation-0'"
:color="step === 0 ? '' : 'grey lighten-2'"
class="d-flex justify-center align-center"
height="740"
>
<v-row
class="fill-height"
align-content="center"
justify="center"
>
<v-col
class="text-center"
cols="12"
>
<span v-if="step > 0" class="text-h4">블루투스 연결 <v-icon color="success" class="pb-4" x-large>mdi-check-bold</v-icon> </span>
<span v-else class="text-h4 "><strong>블루투스</strong>를 연결해주세요</span>
</v-col>
<v-col v-if="step === 0" cols="8">
<v-progress-linear
color="blue-grey"
indeterminate
rounded
height="8"
></v-progress-linear>
</v-col>
</v-row>
</v-card>
</v-col>
<v-col cols="4">
<v-card
:class="step === 1 ?'elevation-12':'elevation-0'"
:color="step === 1 ? '' : 'grey lighten-2'"
class="d-flex justify-center align-center "
height="740"
>
<v-row
class="fill-height"
align-content="center"
justify="center"
>
<v-col
class="text-center"
cols="12"
>
<span v-if="step > 1" class="text-h4">데이터 연동<v-icon color="success" class="pb-4" x-large>mdi-check-bold</v-icon> </span>
<div v-else class="text-h4 "><strong>서버</strong>와 DB 연동</div>
<!-- <p class="text-h4 "><strong>S/N : {{id}}</strong></p></div> -->
</v-col>
<v-col v-if="step === 1" cols="8">
<v-progress-linear
color="blue-grey"
indeterminate
rounded
height="8"
></v-progress-linear>
</v-col>
</v-row>
</v-card>
</v-col>
<v-col cols="4">
<v-card
:class="step === 2 ?'elevation-12':'elevation-0'"
:color="step === 2 ? '' : 'grey lighten-2'"
class="d-flex justify-center align-center"
height="740"
>
<v-row
class="fill-height"
align-content="center"
justify="center"
>
<v-col
class="text-center"
cols="12"
>
<span v-if="step > 2" class="text-h4">세팅 초기화<v-icon color="success" class="pb-4" x-large>mdi-check-bold</v-icon> </span>
<span v-else class="text-h4 "><strong>세팅</strong> 초기화</span>
</v-col>
<v-col v-if="step === 2" cols="8">
<v-progress-linear
color="blue-grey"
indeterminate
rounded
height="8"
></v-progress-linear>
</v-col>
</v-row>
</v-card>
</v-col>
</v-row>
</v-container>
</v-card>
</template>
<script>
import wpoService from '@/service/wpo'
import managerService from '@/service/manager'
import projectService from '@/service/project'
import projectGQL from '@/gql/project'
import aoiGQL from '@/gql/aoi'
import { mapActions } from 'vuex'
import { EventBus } from '@/event-bus'
export default {
data () {
return {
message: '준비중...',
step:0,
id:'',
}
},
props: {
project: Object,
},
created(){
this.getAoi()
this.getAois()
this.network()
},
methods: {
...mapActions([
'getAois',
]),
async getAoi(){
let id = await wpoService.getWpoId() //웹서버에 지정한 WPO ID 가져오기
this.id = id
},
async network(){
EventBus.$emit('openBluetooth')
try{
const msg = new Object()
msg.cmd = "network_check"
let response = await managerService.request(msg)
if(JSON.parse(response.data).status){
EventBus.$emit('closeBluetooth')
setTimeout(() => {
this.step = 1
}, 3000);
}else{
this.network()
}
}catch(e){
// EventBus.$emit('openAlert',`${e.message}`, 'error')
}
},
async projectUpdate(){
let result = await projectGQL.update(this.project)
if(result){
projectService.save({})
setTimeout(() => {
this.step = 2
}, 3000);
}
},
async aoiInit(){
let aoi = this.$store.state.aoi.aois.filter( aoi => { return aoi.serial === this.id } )
let result = await aoiGQL.updateAoi(aoi[0])
if(Object.keys(result).length !== 0){
setTimeout(() => {
this.step = 2
EventBus.$emit('setInit')
}, 3000);
}
},
},
watch:{
async step(current) {
switch(current) {
case 1:
this.projectUpdate()
break;
case 2:
this.aoiInit()
break;
}
}
}
}
</script>
<style>
</style>
\ No newline at end of file
<template>
<v-card
width="100%"
height="850px"
class="elevation-0 d-flex justify-center align-center flex-column"
>
<v-progress-circular
color="blue-grey"
indeterminate
rounded
:size="500"
:width="30"
>
<span class="text-h3 font-weight-bold"> {{message}}</span>
</v-progress-circular>
</v-card>
</template>
<script>
export default {
data () {
return {
}
},
props: {
message: String,
},
}
</script>
<style>
</style>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment