Async/Await refactor in intval lib. FakeConnect needs repair.
This commit is contained in:
parent
c453e7ac43
commit
7eab7675cb
|
@ -18,12 +18,15 @@ async function delay (ms) {
|
||||||
|
|
||||||
async function send (device, cmd) {
|
async function send (device, cmd) {
|
||||||
return new Promise ((resolve, reject) => {
|
return new Promise ((resolve, reject) => {
|
||||||
mcopy.arduino.serial[device].write(cmd, (err, results) => {
|
mcopy.arduino.queue[cmd] = (ms) => {
|
||||||
|
return resolve(ms)
|
||||||
|
}
|
||||||
|
return mcopy.arduino.serial[device].write(cmd, (err, results) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
//console.error(err)
|
//console.error(err)
|
||||||
return reject(err)
|
return reject(err)
|
||||||
}
|
}
|
||||||
return resolve(results)
|
//
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -123,7 +126,6 @@ mcopy.arduino.send = async function (serial, cmd, res) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
mcopy.arduino.lock = true
|
mcopy.arduino.lock = true
|
||||||
mcopy.arduino.queue[cmd] = res
|
|
||||||
await delay(mcopy.cfg.arduino.serialDelay)
|
await delay(mcopy.cfg.arduino.serialDelay)
|
||||||
try {
|
try {
|
||||||
results = await send(device, cmd)
|
results = await send(device, cmd)
|
||||||
|
@ -154,18 +156,20 @@ mcopy.arduino.string = async function (serial, str) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//respond with same char over serial when done
|
//respond with same char over serial when done
|
||||||
mcopy.arduino.end = function (data) {
|
mcopy.arduino.end = async function (data) {
|
||||||
var end = new Date().getTime(),
|
const end = new Date().getTime()
|
||||||
ms = end - mcopy.arduino.timer;
|
const ms = end - mcopy.arduino.timer
|
||||||
|
let complete
|
||||||
if (mcopy.arduino.queue[data] !== undefined) {
|
if (mcopy.arduino.queue[data] !== undefined) {
|
||||||
mcopy.arduino.lock = false;
|
mcopy.arduino.lock = false;
|
||||||
//console.log('Command ' + data + ' took ' + ms + 'ms');
|
//console.log('Command ' + data + ' took ' + ms + 'ms');
|
||||||
mcopy.arduino.queue[data](ms); //execute callback
|
complete = mcopy.arduino.queue[data](ms) //execute callback
|
||||||
eventEmitter.emit('arduino_end', data)
|
eventEmitter.emit('arduino_end', data)
|
||||||
delete mcopy.arduino.queue[data]
|
delete mcopy.arduino.queue[data]
|
||||||
} else {
|
} else {
|
||||||
//console.log('Received stray "' + data + '"'); //silent to user
|
//console.log('Received stray "' + data + '"'); //silent to user
|
||||||
}
|
}
|
||||||
|
return complete
|
||||||
};
|
};
|
||||||
mcopy.arduino.alias = function (serial, device) {
|
mcopy.arduino.alias = function (serial, device) {
|
||||||
console.log(`Making "${serial}" an alias of ${device}`)
|
console.log(`Making "${serial}" an alias of ${device}`)
|
||||||
|
@ -189,16 +193,16 @@ mcopy.arduino.connect = async function (serial, device, confirm) {
|
||||||
}
|
}
|
||||||
console.log(`Opened connection with ${mcopy.arduino.path[serial]} as ${serial}`);
|
console.log(`Opened connection with ${mcopy.arduino.path[serial]} as ${serial}`);
|
||||||
if (!confirm) {
|
if (!confirm) {
|
||||||
mcopy.arduino.serial[device].on('data', async data => {
|
mcopy.arduino.serial[device].on('data', async (data) => {
|
||||||
let d = data.toString('utf8')
|
let d = data.toString('utf8')
|
||||||
d = d.replace(newlineRe, '').replace(returnRe, '')
|
d = d.replace(newlineRe, '').replace(returnRe, '')
|
||||||
mcopy.arduino.end(d)
|
return await mcopy.arduino.end(d)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
mcopy.arduino.serial[device].on('data', data => {
|
mcopy.arduino.serial[device].on('data', async (data) => {
|
||||||
let d = data.toString('utf8')
|
let d = data.toString('utf8')
|
||||||
d = d.replace(newlineRe, '').replace(returnRe, '')
|
d = d.replace(newlineRe, '').replace(returnRe, '')
|
||||||
mcopy.arduino.confirmEnd(d)
|
return await mcopy.arduino.confirmEnd(d)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return resolve(mcopy.arduino.path[serial])
|
return resolve(mcopy.arduino.path[serial])
|
||||||
|
@ -242,31 +246,35 @@ mcopy.arduino.verify = async function () {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
mcopy.arduino.distinguish = async function (callback) {
|
mcopy.arduino.distinguish = async function () {
|
||||||
|
return new Promise(async (resolve, reject) => {
|
||||||
const device = mcopy.arduino.alias['connect']
|
const device = mcopy.arduino.alias['connect']
|
||||||
let writeSuccess
|
let writeSuccess
|
||||||
|
let type
|
||||||
mcopy.arduino.confirmExec = function (err, data) {
|
mcopy.arduino.confirmExec = function (err, data) {
|
||||||
if (data === mcopy.cfg.arduino.cmd.proj_identifier) {
|
if (data === mcopy.cfg.arduino.cmd.proj_identifier) {
|
||||||
callback(null, 'projector');
|
type = 'projector'
|
||||||
} else if (data === mcopy.cfg.arduino.cmd.cam_identifier) {
|
} else if (data === mcopy.cfg.arduino.cmd.cam_identifier) {
|
||||||
callback(null, 'camera');
|
type = 'camera'
|
||||||
} else if (data === mcopy.cfg.arduino.cmd.light_identifier) {
|
} else if (data === mcopy.cfg.arduino.cmd.light_identifier) {
|
||||||
callback(null, 'light')
|
type = 'light'
|
||||||
} else if (data === mcopy.cfg.arduino.cmd.proj_light_identifier) {
|
} else if (data === mcopy.cfg.arduino.cmd.proj_light_identifier) {
|
||||||
callback(null, 'projector,light')
|
type = 'projector,light'
|
||||||
} else if (data === mcopy.cfg.arduino.cmd.proj_cam_light_identifier) {
|
} else if (data === mcopy.cfg.arduino.cmd.proj_cam_light_identifier) {
|
||||||
callback(null, 'projector,camera,light')
|
type = 'projector,camera,light'
|
||||||
} else if (data === mcopy.cfg.arduino.cmd.proj_cam_identifier) {
|
} else if (data === mcopy.cfg.arduino.cmd.proj_cam_identifier) {
|
||||||
callback(null, 'projector,camera')
|
type = 'projector,camera'
|
||||||
}
|
}
|
||||||
|
return resolve(type)
|
||||||
}
|
}
|
||||||
await delay(mcopy.cfg.arduino.serialDelay)
|
await delay(mcopy.cfg.arduino.serialDelay)
|
||||||
try {
|
try {
|
||||||
writeSuccess = await send(device, mcopy.cfg.arduino.cmd.mcopy_identifier)
|
writeSuccess = await send(device, mcopy.cfg.arduino.cmd.mcopy_identifier)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return console.error(e)
|
console.error(e)
|
||||||
|
return reject(e)
|
||||||
}
|
}
|
||||||
return writeSuccess
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
mcopy.arduino.close = async function (callback) {
|
mcopy.arduino.close = async function (callback) {
|
||||||
|
@ -285,18 +293,19 @@ mcopy.arduino.fakeConnect = async function (serial) {
|
||||||
const device = '/dev/fake'
|
const device = '/dev/fake'
|
||||||
mcopy.arduino.alias[serial] = device
|
mcopy.arduino.alias[serial] = device
|
||||||
mcopy.arduino.serial[device] = {
|
mcopy.arduino.serial[device] = {
|
||||||
write : async function (cmd, res) {
|
write : function (cmd, cb) {
|
||||||
return new Promise(async (resolve, reject) => {
|
const t = {
|
||||||
var t = {
|
|
||||||
c : mcopy.cfg.arduino.cam.time + mcopy.cfg.arduino.cam.delay,
|
c : mcopy.cfg.arduino.cam.time + mcopy.cfg.arduino.cam.delay,
|
||||||
p : mcopy.cfg.arduino.proj.time + mcopy.cfg.arduino.proj.delay
|
p : mcopy.cfg.arduino.proj.time + mcopy.cfg.arduino.proj.delay
|
||||||
},
|
}
|
||||||
timeout = t[cmd];
|
let timeout = t[cmd]
|
||||||
if (typeof timeout === 'undefined') timeout = 10;
|
let end
|
||||||
mcopy.arduino.timer = +new Date();
|
if (typeof timeout === 'undefined') timeout = 10
|
||||||
await delay(timeout)
|
mcopy.arduino.timer = +new Date()
|
||||||
return await mcopy.arduino.end(cmd)
|
return setTimeout(() => {
|
||||||
})
|
mcopy.arduino.end(cmd)
|
||||||
|
return cb()
|
||||||
|
}, timeout)
|
||||||
},
|
},
|
||||||
string : async function (str) {
|
string : async function (str) {
|
||||||
//do nothing
|
//do nothing
|
||||||
|
|
|
@ -6,32 +6,35 @@ class Intval {
|
||||||
constructor (url) {
|
constructor (url) {
|
||||||
this._baseUrl = `http://${url}`
|
this._baseUrl = `http://${url}`
|
||||||
}
|
}
|
||||||
move (cb) {
|
async move () {
|
||||||
|
return new Promise ((resolve, reject) => {
|
||||||
const timeStart = +new Date()
|
const timeStart = +new Date()
|
||||||
const url = `${this._baseUrl}/frame`
|
const url = `${this._baseUrl}/frame`
|
||||||
//console.log(url)
|
//console.log(url)
|
||||||
req(url, (err, res, body) => {
|
return req(url, (err, res, body) => {
|
||||||
let ms = (+new Date()) - timeStart
|
let ms = (+new Date()) - timeStart
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error(err)
|
return reject(err)
|
||||||
}
|
}
|
||||||
cb(ms)
|
return resolve(ms)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
setDir (dir, cb) {
|
async setDir (dir) {
|
||||||
|
return new Promise ((resolve, reject) => {
|
||||||
const timeStart = +new Date()
|
const timeStart = +new Date()
|
||||||
const url = `${this._baseUrl}/dir?dir=${dir}`
|
const url = `${this._baseUrl}/dir?dir=${dir}`
|
||||||
//console.log(url)
|
//console.log(url)
|
||||||
req(url, (err, res, body) => {
|
return req(url, (err, res, body) => {
|
||||||
let ms = (+new Date()) - timeStart
|
let ms = (+new Date()) - timeStart
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error(err)
|
return reject(err)
|
||||||
}
|
}
|
||||||
cb(ms)
|
return resolve(ms)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
setExposure (exposure, cb) {
|
async setExposure (exposure, cb) {
|
||||||
const timeStart = +new Date()
|
const timeStart = +new Date()
|
||||||
const url = `${this._baseUrl}/exposure?exposure=${exposure}`
|
const url = `${this._baseUrl}/exposure?exposure=${exposure}`
|
||||||
//console.log(url)
|
//console.log(url)
|
||||||
|
|
116
app/main.js
116
app/main.js
|
@ -95,7 +95,7 @@ dev.favor = function (devices) {
|
||||||
return devices
|
return devices
|
||||||
}
|
}
|
||||||
|
|
||||||
dev.distinguish = async function (device, callback) {
|
dev.distinguish = async function (device) {
|
||||||
let connectSuccess
|
let connectSuccess
|
||||||
let verifySuccess
|
let verifySuccess
|
||||||
let type
|
let type
|
||||||
|
@ -249,6 +249,11 @@ dev.connectDevice = async function (device, type) {
|
||||||
|
|
||||||
//Cases for 1 or 2 arduinos connected
|
//Cases for 1 or 2 arduinos connected
|
||||||
dev.all = async function (devices) {
|
dev.all = async function (devices) {
|
||||||
|
let c = {}
|
||||||
|
let p = {}
|
||||||
|
let l = {}
|
||||||
|
let all
|
||||||
|
|
||||||
dev.connected = {
|
dev.connected = {
|
||||||
projector : false,
|
projector : false,
|
||||||
camera : false,
|
camera : false,
|
||||||
|
@ -257,32 +262,31 @@ dev.all = async function (devices) {
|
||||||
|
|
||||||
let checklist = []
|
let checklist = []
|
||||||
|
|
||||||
/*await Promise.all(devices.map(async (device) => {
|
all = Promise.all(devices.map(async (device) => {
|
||||||
return new Promise(async (resolve, reject) => {
|
return new Promise(async (resolve, reject) => {
|
||||||
let type
|
let type
|
||||||
let d
|
let d
|
||||||
|
|
||||||
try {
|
try {
|
||||||
type = await dev.distinguish(device)
|
type = await dev.distinguish(device)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
return reject(err)
|
return reject(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
d = await dev.connectDevice(device, type)
|
d = await dev.connectDevice(device, type)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
return reject(err)
|
return reject(err)
|
||||||
}
|
}
|
||||||
return resolve(d)
|
|
||||||
|
return d
|
||||||
})
|
})
|
||||||
})*/
|
}))
|
||||||
|
|
||||||
//done checking devices
|
//done checking devices
|
||||||
|
|
||||||
let c = {}
|
|
||||||
let p = {}
|
|
||||||
let l = {}
|
|
||||||
|
|
||||||
if (!dev.connected.projector) {
|
if (!dev.connected.projector) {
|
||||||
await dev.fakeProjector()
|
await dev.fakeProjector()
|
||||||
}
|
}
|
||||||
|
@ -294,7 +298,6 @@ dev.all = async function (devices) {
|
||||||
|
|
||||||
if (mcopy.settings.camera.intval) {
|
if (mcopy.settings.camera.intval) {
|
||||||
c.intval = mcopy.settings.camera.intval
|
c.intval = mcopy.settings.camera.intval
|
||||||
console.dir(mcopy.settings.camera)
|
|
||||||
await delay(1000)
|
await delay(1000)
|
||||||
cam.connectIntval(null, { connect : true, url : c.intval })
|
cam.connectIntval(null, { connect : true, url : c.intval })
|
||||||
}
|
}
|
||||||
|
@ -305,7 +308,7 @@ dev.all = async function (devices) {
|
||||||
|
|
||||||
l.arduino = dev.connected.light
|
l.arduino = dev.connected.light
|
||||||
|
|
||||||
dev.ready(p, c, l)
|
return dev.ready(p, c, l)
|
||||||
}
|
}
|
||||||
|
|
||||||
dev.remember = function (which, device, type) {
|
dev.remember = function (which, device, type) {
|
||||||
|
@ -337,6 +340,7 @@ dev.ready = function (projector, camera, light) {
|
||||||
settings.update('projector', projector)
|
settings.update('projector', projector)
|
||||||
settings.update('light', light)
|
settings.update('light', light)
|
||||||
settings.save()
|
settings.save()
|
||||||
|
return true
|
||||||
};
|
};
|
||||||
|
|
||||||
var createMenu = function () {
|
var createMenu = function () {
|
||||||
|
@ -370,16 +374,20 @@ light.listen = function () {
|
||||||
event.returnValue = true
|
event.returnValue = true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
light.set = function (rgb, id) {
|
light.set = async function (rgb, id) {
|
||||||
var str = rgb.join(',');
|
const str = rgb.join(',');
|
||||||
arduino.send('light', mcopy.cfg.arduino.cmd.light, (ms) => {
|
let ms
|
||||||
light.end(rgb, id, ms)
|
try {
|
||||||
})
|
ms = await arduino.send('light', mcopy.cfg.arduino.cmd.light)
|
||||||
arduino.string('light', str)
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
}
|
}
|
||||||
light.end = function (rgb, id, ms) {
|
await arduino.string('light', str)
|
||||||
|
return await light.end(rgb, id, ms)
|
||||||
|
}
|
||||||
|
light.end = async function (rgb, id, ms) {
|
||||||
log.info('Light set to ' + rgb.join(','), 'LIGHT', true, true)
|
log.info('Light set to ' + rgb.join(','), 'LIGHT', true, true)
|
||||||
mainWindow.webContents.send('light', {rgb: rgb, id : id, ms: ms})
|
return await mainWindow.webContents.send('light', {rgb: rgb, id : id, ms: ms})
|
||||||
}
|
}
|
||||||
|
|
||||||
proj.state = {
|
proj.state = {
|
||||||
|
@ -388,22 +396,31 @@ proj.state = {
|
||||||
proj.init = function () {
|
proj.init = function () {
|
||||||
proj.listen()
|
proj.listen()
|
||||||
}
|
}
|
||||||
proj.set = function (dir, id) {
|
proj.set = async function (dir, id) {
|
||||||
var cmd
|
let cmd
|
||||||
|
let ms
|
||||||
if (dir) {
|
if (dir) {
|
||||||
cmd = mcopy.cfg.arduino.cmd.proj_forward
|
cmd = mcopy.cfg.arduino.cmd.proj_forward
|
||||||
} else {
|
} else {
|
||||||
cmd = mcopy.cfg.arduino.cmd.proj_backward
|
cmd = mcopy.cfg.arduino.cmd.proj_backward
|
||||||
}
|
}
|
||||||
proj.state.dir = dir
|
proj.state.dir = dir
|
||||||
arduino.send('projector', cmd, (ms) => {
|
try {
|
||||||
proj.end(cmd, id, ms)
|
ms = await arduino.send('projector', cmd)
|
||||||
})
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
}
|
}
|
||||||
proj.move = function (frame, id) {
|
return await proj.end(cmd, id, ms)
|
||||||
arduino.send('projector', mcopy.cfg.arduino.cmd.projector, (ms) => {
|
}
|
||||||
proj.end(mcopy.cfg.arduino.cmd.projector, id, ms)
|
proj.move = async function (frame, id) {
|
||||||
})
|
const cmd = mcopy.cfg.arduino.cmd.projector
|
||||||
|
let ms
|
||||||
|
try {
|
||||||
|
ms = await arduino.send('projector', cmd)
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
}
|
||||||
|
return await proj.end(mcopy.cfg.arduino.cmd.projector, id, ms)
|
||||||
}
|
}
|
||||||
proj.listen = function () {
|
proj.listen = function () {
|
||||||
ipcMain.on('proj', (event, arg) => {
|
ipcMain.on('proj', (event, arg) => {
|
||||||
|
@ -415,8 +432,8 @@ proj.listen = function () {
|
||||||
event.returnValue = true
|
event.returnValue = true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
proj.end = function (cmd, id, ms) {
|
proj.end = async function (cmd, id, ms) {
|
||||||
var message = ''
|
let message = ''
|
||||||
if (cmd === mcopy.cfg.arduino.cmd.proj_forward) {
|
if (cmd === mcopy.cfg.arduino.cmd.proj_forward) {
|
||||||
message = 'Projector set to FORWARD'
|
message = 'Projector set to FORWARD'
|
||||||
} else if (cmd === mcopy.cfg.arduino.cmd.proj_backward) {
|
} else if (cmd === mcopy.cfg.arduino.cmd.proj_backward) {
|
||||||
|
@ -431,7 +448,7 @@ proj.end = function (cmd, id, ms) {
|
||||||
message += ' 1 frame'
|
message += ' 1 frame'
|
||||||
}
|
}
|
||||||
log.info(message, 'PROJECTOR', true, true)
|
log.info(message, 'PROJECTOR', true, true)
|
||||||
mainWindow.webContents.send('proj', {cmd: cmd, id : id, ms: ms})
|
return await mainWindow.webContents.send('proj', {cmd: cmd, id : id, ms: ms})
|
||||||
}
|
}
|
||||||
|
|
||||||
cam.intval = null
|
cam.intval = null
|
||||||
|
@ -441,8 +458,9 @@ cam.state = {
|
||||||
cam.init = function () {
|
cam.init = function () {
|
||||||
cam.listen()
|
cam.listen()
|
||||||
}
|
}
|
||||||
cam.set = function (dir, id) {
|
cam.set = async function (dir, id) {
|
||||||
let cmd
|
let cmd
|
||||||
|
let ms
|
||||||
if (dir) {
|
if (dir) {
|
||||||
cmd = mcopy.cfg.arduino.cmd.cam_forward
|
cmd = mcopy.cfg.arduino.cmd.cam_forward
|
||||||
} else {
|
} else {
|
||||||
|
@ -451,27 +469,31 @@ cam.set = function (dir, id) {
|
||||||
cam.state.dir = dir
|
cam.state.dir = dir
|
||||||
|
|
||||||
if (cam.intval) {
|
if (cam.intval) {
|
||||||
cam.intval.setDir(dir, ms => {
|
try {
|
||||||
cam.end(cmd, id, ms)
|
ms = await cam.intval.setDir(dir)
|
||||||
})
|
} catch (err) {
|
||||||
} else {
|
console.error(err)
|
||||||
arduino.send('camera', cmd, ms => {
|
|
||||||
cam.end(cmd, id, ms)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
ms = await arduino.send('camera', cmd)
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return await cam.end(cmd, id, ms)
|
||||||
}
|
}
|
||||||
|
|
||||||
cam.move = function (frame, id) {
|
cam.move = async function (frame, id) {
|
||||||
let cmd = mcopy.cfg.arduino.cmd.camera
|
const cmd = mcopy.cfg.arduino.cmd.camera
|
||||||
|
let ms
|
||||||
if (cam.intval) {
|
if (cam.intval) {
|
||||||
cam.intval.move(ms => {
|
ms = await cam.intval.move()
|
||||||
cam.end(cmd, id, ms)
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
arduino.send('camera', cmd, ms => {
|
ms = await arduino.send('camera', cmd)
|
||||||
cam.end(cmd, id, ms)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
console.log(ms)
|
||||||
|
return cam.end(cmd, id, ms)
|
||||||
}
|
}
|
||||||
|
|
||||||
cam.exposure = function (exposure, id) {
|
cam.exposure = function (exposure, id) {
|
||||||
|
@ -515,7 +537,7 @@ cam.listen = function () {
|
||||||
})
|
})
|
||||||
ipcMain.on('intval', cam.connectIntval)
|
ipcMain.on('intval', cam.connectIntval)
|
||||||
}
|
}
|
||||||
cam.end = function (cmd, id, ms) {
|
cam.end = async function (cmd, id, ms) {
|
||||||
var message = ''
|
var message = ''
|
||||||
if (cmd === mcopy.cfg.arduino.cmd.cam_forward) {
|
if (cmd === mcopy.cfg.arduino.cmd.cam_forward) {
|
||||||
message = 'Camera set to FORWARD'
|
message = 'Camera set to FORWARD'
|
||||||
|
|
Loading…
Reference in New Issue