const fs = require('fs') const { v4: uuidv4 } = require('uuid'); const MAX_LEADERBOARD = 20 // Web Server var express = require('express') var app = express() var router = express.Router() // Upload Files var upload = require('./app/config/multer.config.js') // Static Website app.use(express.static('resources')) var http = require('http').createServer(app) var io = require('socket.io')(http) // Docker Container const {Docker} = require('node-docker-api') const docker = new Docker({ socketPath: '/var/run/docker.sock' }) // Console Log const stripAnsi = require('strip-ansi') // Set up directories global.__basedir = __dirname const path = require('path') current_path = process.cwd() // Initialize Leaderboard global.leaderboard = {} leaderboard.ulysses16 = [] leaderboard.att48 = [] leaderboard.st70 = [] leaderboard.a280 = [] leaderboard.pcb442 = [] leaderboard.dsj1000 = [] var write_leaderboard = () => { fs.writeFile('leaderboard.json', JSON.stringify(leaderboard), (err) => { if (err) throw err console.log('Data written to file') }) } // Read from JSON file first if exists try { if (fs.existsSync('leaderboard.json')) { //file exists console.log("Existed leaderboard") leaderboard = require('./leaderboard.json') console.log(leaderboard) } else { write_leaderboard() } } catch(err) { console.error(err) } var update_leaderboard = (obj) => { // Update each TSP entry if(obj.name === 'ulysses16') { leaderboard.ulysses16.push(obj.data) } else if (obj.name === 'att48') { leaderboard.att48.push(obj.data) } else if (obj.name === 'st70') { leaderboard.st70.push(obj.data) } else if (obj.name === 'a280') { leaderboard.a280.push(obj.data) } else if (obj.name === 'pcb442') { leaderboard.pcb442.push(obj.data) } else if (obj.name === 'dsj1000') { leaderboard.dsj1000.push(obj.data) } // Only accept top MAX_LEADERBOARD results leaderboard.ulysses16.sort(function(a, b){return a.fitness - b.fitness}) leaderboard.ulysses16 = leaderboard.ulysses16.slice(0, MAX_LEADERBOARD) leaderboard.att48.sort(function(a, b){return a.fitness - b.fitness}).slice(0, MAX_LEADERBOARD) leaderboard.att48 = leaderboard.att48.slice(0, MAX_LEADERBOARD) leaderboard.st70.sort(function(a, b){return a.fitness - b.fitness}).slice(0, MAX_LEADERBOARD) leaderboard.st70 = leaderboard.st70.slice(0, MAX_LEADERBOARD) leaderboard.a280.sort(function(a, b){return a.fitness - b.fitness}) leaderboard.a280 = leaderboard.a280.slice(0, MAX_LEADERBOARD) leaderboard.pcb442.sort(function(a, b){return a.fitness - b.fitness}) leaderboard.pcb442 = leaderboard.pcb442.slice(0, MAX_LEADERBOARD) leaderboard.dsj1000.sort(function(a, b){return a.fitness - b.fitness}) leaderboard.dsj1000 = leaderboard.dsj1000.slice(0, MAX_LEADERBOARD) // Write to json file write_leaderboard() } // Get submission results var update_submission = (socket, sessionID) => { var output_dir = current_path.toString() + '/output/' + sessionID + '/' console.log(output_dir) var res = [] if (output_dir.length != 0) { socket.emit('info', 'Got output') // Read output dir files = fs.readdirSync(output_dir) // Read each result in txt file files.forEach(file => { if (path.extname(file) === '.txt') { var i = 0 // Result object var obj = {} obj.name = file try { // read contents of the file const data = fs.readFileSync(output_dir + file, 'UTF-8') // split the contents by new line const lines = data.split(/\r?\n/) lines.forEach((line) => { i = i + 1 // The first line is the fitness if(i == 1) { var fitness = parseFloat(line) if(fitness < 0) { obj.fitness = parseInt(line) } else { obj.fitness = fitness } } // The second line is the path else if (i == 2) { // Only read when there is no error if(obj.fitness > 0) { solution_array = line.split(",") for(var j = 0; j < solution_array.length; j++) { solution_array[i] = parseInt(solution_array[i], 10) } obj.solution = solution_array } else { obj.solution = [] } } }) } catch (err) { console.error(err) } console.log(obj) res.push(obj) } }) if (res.length === 0) { console.log(res) socket.emit('error', 'Please check my_model.py') } socket.emit('result', JSON.stringify(res)) } else { console.log('no result') socket.emit('error', 'Please check my_model.py') } } var check_time = 0 var docker_build = (socket, sessionID, option) => { options = "" if (option.easy) options += " -e " if (option.medium) options += " -m " if (option.difficult) options += " -d " check_time = 0 console.log('[server][start]', sessionID) socket.emit('start') fs.mkdir(current_path + '/output/' + sessionID.toString(), () => { socket.emit('info', 'Output directort created.') socket.emit('info', 'Test started, this may take a while.') }) docker.container.create({ Image: 'com2014-tsp', HostConfig: { Binds: [ '/uploads/' + sessionID.toString() + '/my_model.py:/tsp/model/my_model.py', "/output/" + sessionID.toString() + "/:/output/" ] }, Cmd: ['/bin/bash', '-c', 'python main.py' + options + '; mkdir -p /output && cp -r output/* /output'] }) .then( (container) => { // Start container container_id = container.data.Id socket.emit('info','Container Id: ' + container_id) return container.start() }) .then(container => container.logs({ follow: true, stdout: true, stderr: true })) .then(stream => { // Write container logs to client stream.on('data', (info) => { console.log(stripAnsi(new Buffer.from(info).toString())) socket.emit('info', stripAnsi(new Buffer.from(info).toString('ascii'))) }), stream.on('error', (err) => { console.log(err) socket.emit('info', stripAnsi(new Buffer.from(err).toString('ascii'))) }) }) .catch(error => console.log(error)) // Check if container is still running var intervalObj // Timeout (each container runs only for 60s at most) var timeoutObj var finished = false // Check if container is still running intervalObj = setInterval(() => { if(!finished) { docker.container.list({all:true}) .then((containers) => { containers.forEach(container => { if(container.data.Id == container_id) { if(check_time % 30 == 0) { socket.emit('info', 'Container Status: ' + container.data.State) } if(container.data.State === 'exited') { // Finished Running finished = true clearInterval(intervalObj) clearTimeout(timeoutObj) socket.emit('info', 'Container exited') // Delete container container.delete({ force: true }) // Get submission results update_submission(socket, sessionID) } } }) }) .catch(error => console.log(error)) check_time = check_time + 1 } }, 2000) // Timeout (each container runs only for 60s at most) timeoutObj = setTimeout(() => { console.log('end') if(!finished) { docker.container.list({all:true}) .then((containers) => { containers.forEach(container => { if(container.data.Id == container_id) { socket.emit('info', 'Timeout, cleaning ' + container.data.State + ' container') // Delete container container.delete({ force: true }) // Finished Running finished = true clearInterval(intervalObj) clearTimeout(timeoutObj) clearInterval(intervalObj) socket.emit('info', 'Container removed') } }) }) .catch(error => console.log(error)) } }, 900000) } var clients = 0 io.on('connection', (socket) => { // New client connected const sessionID = socket.id console.log('[client][connection]', sessionID) // Update client number clients = clients + 1 io.sockets.emit('users_count', clients) // Inform new client the leaderboard // console.log(leaderboard) socket.emit('leaderboard', leaderboard) user_option = {} user_option.easy = true user_option.medium = false user_option.difficult = false // Client change build option socket.on('option', (option) => { console.log('[client][option]', option) user_option = option }) // Client disconnected socket.on('disconnect', () => { clients = clients - 1 console.log('[client][disconnect]', sessionID) io.sockets.emit('users_count', clients) }) // Client request a build socket.on('build', () => { console.log('[client][build]', sessionID) docker_build(socket, sessionID, user_option) }) // Client submited a new result socket.on('submit', (obj) => { console.log('[client][submit]', sessionID) obj.data.id = uuidv4() update_leaderboard(obj) io.emit('leaderboard', leaderboard) }) }) require('./app/routers/file.router.js')(app, router, upload) // Create a Server var server = http.listen(8080, () => { var host = server.address().address var port = server.address().port console.log("App listening at http://%s:%s", host, port) })