Merge branch 'feature/make_cp' into develop
This commit is contained in:
commit
0537f04f5e
|
@ -1,3 +1,5 @@
|
|||
node_modules
|
||||
yarn.lock
|
||||
package-lock.json
|
||||
config.json
|
||||
config.default.json
|
|
@ -1,51 +1,58 @@
|
|||
const EventEmitter = require('events')
|
||||
const EventEmitter = require('events') // eslint-disable-line
|
||||
const escpos = require('escpos')
|
||||
const config = require('./config.json')
|
||||
// const config = require('./config.json')
|
||||
const fs = require('fs')
|
||||
const iconv = require('iconv-lite')
|
||||
|
||||
class PrinterDevice {
|
||||
constructor() {
|
||||
class PrinterDevice extends EventEmitter {
|
||||
constructor () {
|
||||
super()
|
||||
this._serial = ''
|
||||
this._feed = 8
|
||||
this._isOpen = false
|
||||
this._device = null
|
||||
this._printer = null
|
||||
this._type = null // type = serial or console
|
||||
this._count = 0
|
||||
this._lastPrint = ''
|
||||
}
|
||||
|
||||
encodeStr(str){
|
||||
if(!str || typeof str != 'string') return false
|
||||
encodeStr (str) {
|
||||
if (!str || typeof str !== 'string') return false
|
||||
return iconv.encode(str, 'big5')
|
||||
}
|
||||
|
||||
async connect() {
|
||||
async connect () {
|
||||
let self = this
|
||||
let chkSerial = await new Promise((resolve, reject) => {
|
||||
fs.access(config.printer.serial, err => {
|
||||
fs.access(self._serial, err => {
|
||||
if (err) return resolve(false)
|
||||
return resolve(true)
|
||||
})
|
||||
})
|
||||
if (chkSerial) {
|
||||
this._device = new escpos.Serial(config.printer.serial)
|
||||
this._device = new escpos.Serial(this._serial)
|
||||
this._type = 'serial'
|
||||
} else {
|
||||
this._device = new escpos.Console()
|
||||
this._type = 'console'
|
||||
}
|
||||
this._printer = new escpos.Printer(this._device)
|
||||
this.openSerial()
|
||||
await this.openSerial()
|
||||
}
|
||||
|
||||
async openSerial() {
|
||||
if (!this._device || this._type != 'serial') return
|
||||
async openSerial () {
|
||||
let self = this
|
||||
if (!this._device || this._type !== 'serial') return
|
||||
this._isOpen = await new Promise((resolve, reject) => {
|
||||
this._device.open(err => {
|
||||
self._device.open(err => {
|
||||
if (err) return resolve(false)
|
||||
return resolve(true)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async printerString(str) {
|
||||
async printerString (str) {
|
||||
if (!this._isOpen || !this._printer) return false
|
||||
|
||||
this._printer.font('a')
|
||||
|
@ -66,8 +73,8 @@ class PrinterDevice {
|
|||
case 's':
|
||||
let size = tmp.substring(2)
|
||||
let sarr = size.trim().split(',')
|
||||
if (sarr.length != 2) break
|
||||
if(!isFinite(sarr[0]) || !isFinite(sarr[1])) break
|
||||
if (sarr.length !== 2) break
|
||||
if (!isFinite(sarr[0]) || !isFinite(sarr[1])) break
|
||||
sarr = sarr.map(t => Math.floor(t))
|
||||
this._printer.size(sarr[0], sarr[1])
|
||||
break
|
||||
|
@ -77,26 +84,74 @@ class PrinterDevice {
|
|||
}
|
||||
}
|
||||
|
||||
this._printer.cut(true, config.printer.cutFeed || 4)
|
||||
this._printer.cut(true, this._feed || 4)
|
||||
|
||||
this._lastPrint = str
|
||||
this.addCount()
|
||||
this.emit('printDone')
|
||||
return true
|
||||
}
|
||||
|
||||
async close() {
|
||||
async printTestPage () {
|
||||
await this.connect()
|
||||
if (!this._isOpen) return false
|
||||
this._printer.align('ct')
|
||||
this._printer.size(1, 2)
|
||||
this._printer.text('Print Test Page')
|
||||
this._printer.align('lt')
|
||||
this._printer.size(1, 1)
|
||||
this._printer.text('Test Page Content')
|
||||
this._printer.cut(true, this._feed)
|
||||
await this.close()
|
||||
return true
|
||||
}
|
||||
|
||||
async close () {
|
||||
if (!this._device) return
|
||||
if (this._type == 'serial') {
|
||||
if (this._type === 'serial') {
|
||||
let self = this
|
||||
await new Promise((resolve, reject) => {
|
||||
self._printer.close(() => {
|
||||
self._printer = null
|
||||
self._isOpen = false
|
||||
resolve(1)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
get isOpen() {
|
||||
return this._isOpen ? true : false
|
||||
addCount () {
|
||||
this._count++
|
||||
}
|
||||
|
||||
get isOpen () {
|
||||
return !!this._isOpen
|
||||
}
|
||||
|
||||
set serial (str) {
|
||||
this._serial = str
|
||||
}
|
||||
|
||||
get serial () {
|
||||
return this._serial
|
||||
}
|
||||
|
||||
set feed (str) {
|
||||
if (!isFinite(str)) return
|
||||
this._feed = Math.floor(parseInt(str))
|
||||
}
|
||||
|
||||
get feed () {
|
||||
return this._feed
|
||||
}
|
||||
|
||||
get count () {
|
||||
return this._count
|
||||
}
|
||||
|
||||
get lastPrint () {
|
||||
return this._lastPrint || ''
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new PrinterDevice()
|
||||
module.exports = new PrinterDevice()
|
||||
|
|
63
app.js
63
app.js
|
@ -1,59 +1,10 @@
|
|||
const bleno = require('bleno')
|
||||
const config = require('./config.json')
|
||||
const adapterName = 'BLE_Printer'
|
||||
const serverUUID = config.uuid.main
|
||||
const localEvent = require('./localEvent')
|
||||
const fs = require('fs')
|
||||
const MainService = require('./main-service')
|
||||
// set global env
|
||||
process.env.PROJECT_ROOT = __dirname
|
||||
|
||||
const printer = require('./PrinterDev')
|
||||
require('top-level-await')
|
||||
|
||||
async function initSystem(){
|
||||
try{
|
||||
await printer.connect()
|
||||
}catch(err){
|
||||
throw err
|
||||
}
|
||||
try {
|
||||
require('./server')
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
|
||||
initSystem()
|
||||
|
||||
bleno.on('stateChange', state => {
|
||||
console.log(`bt device state ${state}`)
|
||||
if (state == 'poweredOn') {
|
||||
|
||||
bleno.startAdvertising(adapterName, [serverUUID], (error) => {
|
||||
|
||||
})
|
||||
} else {
|
||||
bleno.stopAdvertising()
|
||||
}
|
||||
})
|
||||
|
||||
bleno.on('advertisingStart', function (error) {
|
||||
console.log('on -> advertisingStart: ' + (error ? 'error ' + error : 'success'));
|
||||
|
||||
if (!error) {
|
||||
bleno.setServices([new MainService()], error => {
|
||||
console.log('set service', error)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
localEvent.on('print', async (str) => {
|
||||
let status = false
|
||||
if (printer.isOpen) {
|
||||
try {
|
||||
status = await printer.printerString(str)
|
||||
} catch (err) {
|
||||
status = false
|
||||
}
|
||||
}
|
||||
|
||||
localEvent.emit('printResult', status)
|
||||
})
|
||||
|
||||
process.on('SIGINT', () => {
|
||||
printer.close()
|
||||
process.exit(0)
|
||||
})
|
|
@ -1,6 +1,6 @@
|
|||
const bleno = require('bleno')
|
||||
const config = require('./config.json')
|
||||
const localEvent = require('./localEvent')
|
||||
const config = require('../config.json')
|
||||
const localEvent = require('../localEvent')
|
||||
|
||||
var tmp = []
|
||||
var idx = 0
|
||||
|
@ -12,9 +12,9 @@ const notifyDesciptor = new bleno.Descriptor({
|
|||
})
|
||||
|
||||
class DataCharacteristic extends bleno.Characteristic {
|
||||
constructor() {
|
||||
constructor () {
|
||||
super({
|
||||
uuid: config.uuid.service.func.data,
|
||||
uuid: config.ble.uuid.characteristic,
|
||||
properties: ['write', 'writeWithoutResponse', 'notify'],
|
||||
descriptors: [
|
||||
notifyDesciptor
|
||||
|
@ -23,15 +23,15 @@ class DataCharacteristic extends bleno.Characteristic {
|
|||
this._notifyFunc = null
|
||||
}
|
||||
|
||||
onWriteRequest(data, offset, withoutResponse, callback) {
|
||||
onWriteRequest (data, offset, withoutResponse, callback) {
|
||||
console.log(`offset :: ${offset} , withoutRes :: ${withoutResponse}`)
|
||||
console.log(`get data`, data)
|
||||
|
||||
if (data.length == 1 && data[0] == 0x00) {
|
||||
if (data.length === 1 && data[0] === 0x00) {
|
||||
tmp = []
|
||||
idx = 0
|
||||
failFlag = false
|
||||
} else if (data.length == 1 && data[0] == 0xff) {
|
||||
} else if (data.length === 1 && data[0] === 0xff) {
|
||||
console.log(Buffer.from(tmp).toString())
|
||||
if (!failFlag) {
|
||||
localEvent.emit('print', Buffer.from(tmp).toString())
|
||||
|
@ -53,17 +53,17 @@ class DataCharacteristic extends bleno.Characteristic {
|
|||
}
|
||||
}
|
||||
|
||||
sendNotidy(str) {
|
||||
sendNotidy (str) {
|
||||
if (!this._notifyFunc) return
|
||||
this._notifyFunc(Buffer.from(str))
|
||||
}
|
||||
|
||||
onSubscribe(maxValueSize, cb) {
|
||||
onSubscribe (maxValueSize, cb) {
|
||||
console.log(maxValueSize)
|
||||
this._notifyFunc = cb
|
||||
}
|
||||
|
||||
onUnsubscribe() {
|
||||
onUnsubscribe () {
|
||||
this._notifyFunc = null
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
const config = require('../config.json')
|
||||
const bleno = require('bleno')
|
||||
const adapterName = 'BLE_Printer'
|
||||
const serverUUID = config.ble.uuid.main
|
||||
const MainService = require('./main-service')
|
||||
|
||||
bleno.on('stateChange', state => {
|
||||
console.log(`bt device state ${state}`)
|
||||
if (state === 'poweredOn') {
|
||||
bleno.startAdvertising(adapterName, [serverUUID])
|
||||
} else {
|
||||
bleno.stopAdvertising()
|
||||
}
|
||||
})
|
||||
|
||||
bleno.on('advertisingStart', function (error) {
|
||||
console.log('on -> advertisingStart: ' + (error ? 'error ' + error : 'success'))
|
||||
|
||||
if (!error) {
|
||||
bleno.setServices([new MainService()], error => {
|
||||
console.log('set service', error)
|
||||
})
|
||||
}
|
||||
})
|
|
@ -1,14 +1,14 @@
|
|||
const bleno = require('bleno')
|
||||
const config = require('./config.json')
|
||||
const localEvent = require('./localEvent')
|
||||
const config = require('../config.json')
|
||||
const localEvent = require('../localEvent')
|
||||
|
||||
const GdataCharacteristic = require('./gdata-characteristic')
|
||||
const dataCharacteristic = new GdataCharacteristic()
|
||||
|
||||
class MainService extends bleno.PrimaryService {
|
||||
constructor() {
|
||||
constructor () {
|
||||
super({
|
||||
uuid: config.uuid.service.id,
|
||||
uuid: config.ble.uuid.service,
|
||||
characteristics: [
|
||||
dataCharacteristic
|
||||
]
|
15
config.json
15
config.json
|
@ -1,15 +0,0 @@
|
|||
{
|
||||
"uuid":{
|
||||
"main": "930da73d-1b80-4d3e-a8b6-d9b4e7e771bd",
|
||||
"service":{
|
||||
"id":"dd535b71-8f05-4e30-beb0-6fa7ea3dfc3e",
|
||||
"func":{
|
||||
"data":"00000001-8f05-4e30-beb0-6fa7ea3dfc3e"
|
||||
}
|
||||
}
|
||||
},
|
||||
"printer": {
|
||||
"serial": "/dev/ttyUSB0",
|
||||
"cutFeed": 8
|
||||
}
|
||||
}
|
|
@ -1,10 +1,10 @@
|
|||
const EventEmitter = require('events')
|
||||
const util = require('util')
|
||||
const util = require('util') //eslint-disable-line
|
||||
|
||||
class LocalEvent extends EventEmitter {
|
||||
constructor(){
|
||||
constructor () { // eslint-disable-line
|
||||
super()
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new LocalEvent()
|
||||
module.exports = new LocalEvent()
|
||||
|
|
18
package.json
18
package.json
|
@ -3,10 +3,26 @@
|
|||
"version": "0.0.1",
|
||||
"main": "app.js",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"check": "standard --fix --verbose"
|
||||
},
|
||||
"dependencies": {
|
||||
"bleno": "^0.4.2",
|
||||
"escpos": "^2.4.3",
|
||||
"iconv-lite": "^0.4.18",
|
||||
"uuid": "^3.1.0"
|
||||
"kcors": "^2.2.1",
|
||||
"koa": "^2.3.0",
|
||||
"koa-body": "^2.3.0",
|
||||
"koa-ejs": "^4.1.0",
|
||||
"koa-morgan": "^1.0.1",
|
||||
"koa-mount": "^3.0.0",
|
||||
"koa-router": "^7.2.1",
|
||||
"koa-static": "^4.0.1",
|
||||
"top-level-await": "^1.1.0",
|
||||
"uuid": "^3.1.0",
|
||||
"ws": "^3.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"standard": "^10.0.3"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
html, body{
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
.t-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#qr img {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.clearfix:before,
|
||||
.clearfix:after {
|
||||
content: "";
|
||||
display: table;
|
||||
}
|
||||
.clearfix:after {
|
||||
clear: both;
|
||||
}
|
||||
.clearfix {
|
||||
*zoom: 1; /*For IE 6&7 only*/
|
||||
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,139 @@
|
|||
/* eslint-disable no-throw-literal */
|
||||
/* eslint-disable */
|
||||
const KoaRouter = require('koa-router')
|
||||
const router = new KoaRouter()
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const Printer = require('../PrinterDev')
|
||||
const KoaBody = require('koa-body')
|
||||
const uuid = require('uuid')
|
||||
const config = require('../config.json')
|
||||
/* eslint-enable */
|
||||
|
||||
router.use(async (c, n) => {
|
||||
c.data = {
|
||||
title: 'Printer System Dashboard'
|
||||
}
|
||||
let status = 1
|
||||
try {
|
||||
await n()
|
||||
} catch (err) {
|
||||
status = 0
|
||||
if (typeof err === 'string') {
|
||||
c.body = err
|
||||
} else {
|
||||
c.body = err.toString()
|
||||
}
|
||||
}
|
||||
|
||||
if (c.async) {
|
||||
c.body = {
|
||||
status,
|
||||
msg: c.body
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
router.get('/', async (c, n) => {
|
||||
c.data.route = 'index'
|
||||
await c.render('dashboard/index', c.data)
|
||||
})
|
||||
|
||||
router.get('/setting', async (c, n) => {
|
||||
c.data.route = 'setting'
|
||||
|
||||
let ttys = await new Promise((resolve, reject) => {
|
||||
fs.readdir(path.resolve('/dev'), (err, list) => {
|
||||
if (err) return resolve([])
|
||||
let arr = []
|
||||
for (let it of list) {
|
||||
if (/^tty[A-z]/.test(it)) {
|
||||
arr.push(path.resolve('/dev', it))
|
||||
}
|
||||
}
|
||||
resolve(arr)
|
||||
})
|
||||
})
|
||||
c.data.ttys = ttys
|
||||
|
||||
c.data.defConfig = config
|
||||
|
||||
await c.render('dashboard/index', c.data)
|
||||
})
|
||||
|
||||
router.post('/api/print', KoaBody(), async (c, n) => {
|
||||
c.async = true
|
||||
let arr = c.request.body
|
||||
if (!arr.data) throw 'print data empty'
|
||||
if (config.api.secret && !arr.key) throw 'access key enpty'
|
||||
if (config.api.secret !== arr.key.trim()) throw 'access key error'
|
||||
|
||||
let status = await Printer.printerString(arr.data)
|
||||
|
||||
if (!status) throw 'printer fail'
|
||||
|
||||
c.body = 'print success'
|
||||
})
|
||||
|
||||
.post('/api/printer_test', KoaBody(), async (c, n) => {
|
||||
c.async = true
|
||||
let arr = c.request.body
|
||||
if (!arr.device) throw '請輸入印表機連接埠'
|
||||
let device = arr.device
|
||||
let feed = arr.feed || 8
|
||||
if (!isFinite(feed) || feed < 0) throw '切紙前換行必須是整數'
|
||||
feed = typeof feed !== 'number' ? Math.floor(parseInt(feed)) : feed
|
||||
|
||||
let oriDev = Printer.serial
|
||||
let oriFeed = Printer.feed
|
||||
|
||||
if (Printer.isOpen) {
|
||||
await Printer.close()
|
||||
}
|
||||
|
||||
Printer.serial = device
|
||||
Printer.feed = feed
|
||||
|
||||
let status = await Printer.printTestPage()
|
||||
|
||||
if (!status) c.body = '測試失敗'
|
||||
else c.body = '測試成功'
|
||||
|
||||
Printer.serial = oriDev
|
||||
Printer.feed = oriFeed
|
||||
await Printer.connect()
|
||||
})
|
||||
.post('/api/write_config', KoaBody(), async (c, n) => {
|
||||
c.async = true
|
||||
let arr = c.request.body
|
||||
if (!arr.tty) throw '請輸入印表機連接埠'
|
||||
if (!arr.ble || (arr.ble !== 'yes' && arr.ble !== 'no')) throw '請輸入藍牙啟用設定'
|
||||
let feed = arr.feed || 8
|
||||
if (!isFinite(feed) || feed < 0) throw '切紙前換行必須是整數'
|
||||
feed = typeof feed !== 'number' ? Math.floor(parseInt(feed)) : feed
|
||||
|
||||
let json = Object.assign({}, config)
|
||||
|
||||
json.ble.enable = arr.ble === 'yes'
|
||||
|
||||
json.printer.serial = arr.tty
|
||||
json.printer.feed = feed
|
||||
|
||||
json.api.secret = arr.secret || ''
|
||||
|
||||
let wconfig = await new Promise((resolve, reject) => {
|
||||
fs.writeFile(path.resolve(process.env.PROJECT_ROOT, 'config.json'), JSON.stringify(json, null, 2), {
|
||||
mode: 0o664,
|
||||
encoding: 'utf8',
|
||||
flag: 'w'
|
||||
}, err => {
|
||||
if (err) return resolve(false)
|
||||
return resolve(true)
|
||||
})
|
||||
})
|
||||
|
||||
if (!wconfig) throw '設定檔寫入失敗'
|
||||
c.body = '設定檔寫入成功'
|
||||
})
|
||||
|
||||
module.exports = router
|
|
@ -0,0 +1,124 @@
|
|||
/* eslint-disable no-throw-literal */
|
||||
const KoaRouter = require('koa-router')
|
||||
const router = new KoaRouter()
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const Printer = require('../PrinterDev')
|
||||
const KoaBody = require('koa-body')
|
||||
const uuid = require('uuid')
|
||||
|
||||
router.use(async (c, n) => {
|
||||
c.data = {
|
||||
title: 'Printer System Setup'
|
||||
}
|
||||
let status = 1
|
||||
try {
|
||||
await n()
|
||||
} catch (err) {
|
||||
status = 0
|
||||
if (typeof err === 'string') {
|
||||
c.body = err
|
||||
} else {
|
||||
c.body = err.toString()
|
||||
}
|
||||
}
|
||||
|
||||
if (c.async) {
|
||||
c.body = {
|
||||
status,
|
||||
msg: c.body
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
router
|
||||
.get('/', async (c, n) => {
|
||||
c.redirect('/install')
|
||||
})
|
||||
.get('/install', async (c, n) => {
|
||||
// get ttys
|
||||
let ttys = await new Promise((resolve, reject) => {
|
||||
fs.readdir(path.resolve('/dev'), (err, list) => {
|
||||
if (err) return resolve([])
|
||||
let arr = []
|
||||
for (let it of list) {
|
||||
if (/^tty[A-z]/.test(it)) {
|
||||
arr.push(path.resolve('/dev', it))
|
||||
}
|
||||
}
|
||||
resolve(arr)
|
||||
})
|
||||
})
|
||||
c.data.ttys = ttys
|
||||
await c.render('install/index', c.data)
|
||||
})
|
||||
.post('/install/printer_test', KoaBody(), async (c, n) => {
|
||||
c.async = true
|
||||
let arr = c.request.body
|
||||
if (!arr.device) throw '請輸入印表機連接埠'
|
||||
let device = arr.device
|
||||
let feed = arr.feed || 8
|
||||
if (!isFinite(feed) || feed < 0) throw '切紙前換行必須是整數'
|
||||
feed = typeof feed !== 'number' ? Math.floor(parseInt(feed)) : feed
|
||||
|
||||
Printer.serial = device
|
||||
Printer.feed = feed
|
||||
|
||||
let status = await Printer.printTestPage()
|
||||
|
||||
if (!status) c.body = '測試失敗'
|
||||
else c.body = '測試成功'
|
||||
})
|
||||
.post('/install/write_config', KoaBody(), async (c, n) => {
|
||||
c.async = true
|
||||
let arr = c.request.body
|
||||
if (!arr.tty) throw '請輸入印表機連接埠'
|
||||
if (!arr.ble || (arr.ble !== 'yes' && arr.ble !== 'no')) throw '請輸入藍牙啟用設定'
|
||||
let feed = arr.feed || 8
|
||||
if (!isFinite(feed) || feed < 0) throw '切紙前換行必須是整數'
|
||||
feed = typeof feed !== 'number' ? Math.floor(parseInt(feed)) : feed
|
||||
|
||||
let json = {
|
||||
ble: {
|
||||
enable: false,
|
||||
uuid: {
|
||||
main: '',
|
||||
service: '',
|
||||
characteristic: ''
|
||||
}
|
||||
},
|
||||
printer: {
|
||||
serial: '',
|
||||
feed: 8
|
||||
},
|
||||
api: {
|
||||
secret: ''
|
||||
}
|
||||
}
|
||||
|
||||
json.ble.enable = arr.ble === 'yes'
|
||||
json.ble.uuid.main = uuid.v4()
|
||||
json.ble.uuid.service = uuid.v4()
|
||||
json.ble.uuid.characteristic = uuid.v4()
|
||||
|
||||
json.printer.serial = arr.tty
|
||||
json.printer.feed = feed
|
||||
|
||||
json.api.secret = arr.secret || ''
|
||||
|
||||
let wconfig = await new Promise((resolve, reject) => {
|
||||
fs.writeFile(path.resolve(process.env.PROJECT_ROOT, 'config.json'), JSON.stringify(json, null, 2), {
|
||||
mode: 0o664,
|
||||
encoding: 'utf8',
|
||||
flag: 'w'
|
||||
}, err => {
|
||||
if (err) return resolve(false)
|
||||
return resolve(true)
|
||||
})
|
||||
})
|
||||
|
||||
if (!wconfig) throw '設定檔寫入失敗'
|
||||
c.body = '設定檔寫入成功'
|
||||
})
|
||||
|
||||
module.exports = router
|
|
@ -0,0 +1,164 @@
|
|||
const Koa = require('koa')
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const localEvent = require('./localEvent')
|
||||
const WebSocket = require('ws')
|
||||
const exec = require('child_process').exec
|
||||
const Printer = require('./PrinterDev')
|
||||
|
||||
// koa middleware
|
||||
const KoaLogger = require('koa-morgan')
|
||||
const KoaEjs = require('koa-ejs')
|
||||
const KoaMount = require('koa-mount')
|
||||
const KoaStatic = require('koa-static')
|
||||
const KoaRouter = require('koa-router')
|
||||
const Kcors = require('kcors')
|
||||
|
||||
const app = new Koa()
|
||||
|
||||
let config = {}
|
||||
let setupMode = false
|
||||
|
||||
try {
|
||||
let chk = new Promise((resolve, reject) => {
|
||||
fs.access(path.resolve(process.env.PROJECT_ROOT, 'config.json'), (err) => {
|
||||
if (err) {
|
||||
setupMode = true
|
||||
resolve(0)
|
||||
return
|
||||
}
|
||||
config = require('./config.json')
|
||||
resolve(1)
|
||||
})
|
||||
})
|
||||
await chk //eslint-disable-line
|
||||
} catch (err) {
|
||||
setupMode = true
|
||||
}
|
||||
|
||||
const server = app.listen(config.port || 10230, () => {
|
||||
console.log(`Server start on port ${server.address().port}`)
|
||||
})
|
||||
|
||||
const ws = new WebSocket.Server({ server })
|
||||
|
||||
app.use(Kcors())
|
||||
app.use(KoaLogger('short'))
|
||||
app.use(KoaStatic(path.resolve(__dirname, 'public')))
|
||||
KoaEjs(app, {
|
||||
root: path.resolve(__dirname, 'views'),
|
||||
layout: false,
|
||||
viewExt: 'ejs',
|
||||
cache: false,
|
||||
debug: false
|
||||
})
|
||||
|
||||
let router = null
|
||||
|
||||
if (setupMode) {
|
||||
console.log(`start setup mode`)
|
||||
router = require('./route/install')
|
||||
} else {
|
||||
console.log(`start normal mode`)
|
||||
router = require('./route/dashboard')
|
||||
|
||||
Printer.serial = config.printer.serial
|
||||
Printer.feed = config.printer.feed
|
||||
await Printer.connect()
|
||||
|
||||
if (config.ble.enable) {
|
||||
require('./ble')
|
||||
}
|
||||
}
|
||||
|
||||
if (router !== null) {
|
||||
// set Route
|
||||
app.use(router.routes())
|
||||
app.use(router.allowedMethods())
|
||||
|
||||
router.put('/reboot_sys', async (c, n) => {
|
||||
setTimeout(function () {
|
||||
process.exit(1)
|
||||
}, 2000)
|
||||
c.body = '1'
|
||||
})
|
||||
}
|
||||
|
||||
// listen print event
|
||||
localEvent.on('print', async str => {
|
||||
let chk = await Printer.printerString(str)
|
||||
localEvent.emit('printResult', chk)
|
||||
})
|
||||
|
||||
let systemStatus = {
|
||||
ble: {
|
||||
enable: false,
|
||||
mac: '',
|
||||
service: '',
|
||||
characteristic: ''
|
||||
},
|
||||
printer: {
|
||||
connect: false,
|
||||
serial: '',
|
||||
feed: 0
|
||||
},
|
||||
pcount: 0,
|
||||
lastprint: '',
|
||||
secret: ''
|
||||
}
|
||||
|
||||
systemStatus.ble.enable = config.ble.enable
|
||||
systemStatus.secret = config.api.secret
|
||||
if (config.ble.enable) {
|
||||
systemStatus.ble.mac = await getBTAddr()
|
||||
systemStatus.ble.service = config.ble.uuid.service
|
||||
systemStatus.ble.characteristic = config.ble.uuid.characteristic
|
||||
}
|
||||
function getPrinterStatus() {
|
||||
systemStatus.printer.connect = Printer.isOpen
|
||||
systemStatus.printer.serial = Printer.serial
|
||||
systemStatus.printer.feed = Printer.feed
|
||||
systemStatus.pcount = Printer.count
|
||||
systemStatus.lastprint = Printer.lastPrint
|
||||
}
|
||||
getPrinterStatus()
|
||||
|
||||
ws.on('connection', async (client, req) => {
|
||||
|
||||
getPrinterStatus()
|
||||
client.send(JSON.stringify({ type: 'status', data: systemStatus }))
|
||||
|
||||
Printer.on('printDone', () => {
|
||||
getPrinterStatus()
|
||||
client.send(JSON.stringify({ type: 'status', data: systemStatus }))
|
||||
})
|
||||
|
||||
client.on('message', msg => {
|
||||
let m = {}
|
||||
try {
|
||||
m = JSON.parse(msg)
|
||||
} catch (err) {
|
||||
return
|
||||
}
|
||||
|
||||
switch (m.type) {
|
||||
case 'status':
|
||||
client.send(JSON.stringify({ type: 'status', data: systemStatus }))
|
||||
break
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
async function getBTAddr() {
|
||||
let address = await new Promise((resolve, reject) => {
|
||||
exec('bt-adapter -i | grep -i address', (err, sout, serr) => {
|
||||
if (err) return resolve('')
|
||||
resolve(sout)
|
||||
})
|
||||
})
|
||||
|
||||
address = address.trim()
|
||||
let arr = address.split(' ')
|
||||
if (arr.length !== 2) return ''
|
||||
return arr[1].trim()
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
const bleno = require('bleno')
|
||||
const config = require('./config.json')
|
||||
|
||||
class TimeCharacteristic extends bleno.Characteristic {
|
||||
constructor() {
|
||||
super({
|
||||
uuid: config.uuid.service.func.time,
|
||||
properties: ['read'],
|
||||
descriptors: [
|
||||
new bleno.Descriptor({
|
||||
uuid: '88ff',
|
||||
value: 'Now Time'
|
||||
})
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
onReadRequest(offset, callback) {
|
||||
console.log(`offset :: ${offset}`)
|
||||
callback(this.RESULT_SUCCESS, new Buffer(`${Date.now()}`))
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = TimeCharacteristic
|
|
@ -0,0 +1,28 @@
|
|||
<%- include ../includes/header.ejs %>
|
||||
|
||||
<div class="ui container" style="padding-top: 20px;">
|
||||
<div class="ui grid">
|
||||
<div class="row">
|
||||
<div class="three wide column">
|
||||
<div class="ui vertical menu">
|
||||
<a href="/" class="item">Dashboard</a>
|
||||
<div class="item">
|
||||
<div class="header">系統功能</div>
|
||||
<div class="menu">
|
||||
<a href="/setting" class="item <%= route == 'setting' ? 'active' : '' %>">修改設定</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="thirteen wide column">
|
||||
<% if(route == 'index'){ %>
|
||||
<%- include statusview.ejs %>
|
||||
<% } else { %>
|
||||
<%- include setting.ejs %>
|
||||
<% } %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%- include ../includes/footer.ejs %>
|
|
@ -0,0 +1,131 @@
|
|||
|
||||
<div class="ui container" style="padding-top: 20px;">
|
||||
<h1 class="ui header">系統安裝設定</h1>
|
||||
<form id="setup" class="ui form">
|
||||
<h3 class="ui dividing header">印表機設定</h3>
|
||||
<div class="field">
|
||||
<label for="tty">印表機連接埠</label>
|
||||
<select name="tty" id="tty">
|
||||
<option value="">選擇裝置</option>
|
||||
<% for(let i of ttys) { %>
|
||||
<option value="<%= i %>" <%= ttys[i] == defConfig.printer.serial ? 'selected' : '' %>><%= i %></option>
|
||||
<% } %>
|
||||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="feed">切紙前空行</label>
|
||||
<input type="text" name="feed" value="<%= defConfig.printer.feed %>" id="feed">
|
||||
</div>
|
||||
<div class="field">
|
||||
<button class="ui button mini" type="button" id="test-printer">測試印表機</button>
|
||||
</div>
|
||||
|
||||
<h3 class="ui dividing header">藍芽列印設定</h3>
|
||||
<div class="field">
|
||||
<div class="ui checkbox">
|
||||
<input type="checkbox" class="hidden" id="en-ble" <%= defConfig.ble.enable ? 'checked' : '' %>>
|
||||
<label for="en-ble">啟用藍芽</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3 class="ui dividing header">WebAPI設定</h3>
|
||||
<div class="field">
|
||||
<label for="api-secret">API密鑰</label>
|
||||
<input type="text" name="api-secret" placeholder="請輸入密鑰,空白不設定" id="api-secret">
|
||||
</div>
|
||||
|
||||
<div style="margin-top: 40px; text-align: right;">
|
||||
<button class="ui button blue" type="submit">送出設定</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="ui dimmer" id="loader">
|
||||
<div class="ui loader text">Rebooting...</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function getValue(obj){
|
||||
if(!obj || !('value' in obj)) return ''
|
||||
return obj.value
|
||||
}
|
||||
|
||||
$('#test-printer').click(function(){
|
||||
let json = {
|
||||
device: '',
|
||||
feed: 0
|
||||
}
|
||||
let dev = getValue(document.querySelector('select#tty'))
|
||||
let feed = getValue(document.querySelector('input#feed'))
|
||||
|
||||
if(!dev) return alert('請選擇印表機連接埠')
|
||||
if (!isFinite(feed) || feed < 0) return alert('切紙前空行請輸入整數')
|
||||
|
||||
json.device = dev
|
||||
json.feed = Math.floor(parseInt(feed))
|
||||
|
||||
$.ajax({
|
||||
url: '/api/printer_test',
|
||||
type: 'post',
|
||||
data: json,
|
||||
success: function(e){
|
||||
alert(e.msg || '資料傳送失敗')
|
||||
}
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
$('form#setup').submit(function(e) {
|
||||
e.preventDefault()
|
||||
|
||||
let json = {
|
||||
tty: '',
|
||||
feed: 0,
|
||||
ble: '',
|
||||
secret: ''
|
||||
}
|
||||
let dev = getValue(document.querySelector('select#tty'))
|
||||
let feed = getValue(document.querySelector('input#feed'))
|
||||
let elBle = document.querySelector('input#en-ble')
|
||||
let ble = ''
|
||||
if(elBle != null && 'checked' in elBle) ble = elBle.checked ? 'yes' : 'no'
|
||||
|
||||
if(!dev) return alert('請選擇印表機連接埠')
|
||||
if (!isFinite(feed) || feed < 0) return alert('切紙前空行請輸入整數')
|
||||
|
||||
let secret = getValue(document.querySelector('input#api-secret'))
|
||||
|
||||
json.tty = dev
|
||||
json.feed = Math.floor(parseInt(feed))
|
||||
json.ble = ble
|
||||
json.secret = secret
|
||||
|
||||
$.ajax({
|
||||
url: '/api/write_config',
|
||||
type: 'post',
|
||||
data: json,
|
||||
success: function(e){
|
||||
if(e.status != 1) {
|
||||
alert(e.msg || '寫入失敗')
|
||||
return
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: '/reboot_sys',
|
||||
type: 'put',
|
||||
success: function(){
|
||||
setTimeout(function(){
|
||||
location.replace('/')
|
||||
}, 30000)
|
||||
$('#loader').addClass('active')
|
||||
},
|
||||
error: function(){
|
||||
alert('系統重新啟動失敗')
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
return false
|
||||
})
|
||||
</script>
|
|
@ -0,0 +1,152 @@
|
|||
<div class="ui container">
|
||||
<div class="ui grid">
|
||||
<div class="column sixteen wide t-right">
|
||||
<div class="ui segment">
|
||||
<button class="ui button mini" type="button" id="gen-api-qr">Generate API QR</button>
|
||||
<button class="ui button mini" type="button" id="gen-ble-qr">Generate BLE QR</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column sixteen wide">
|
||||
<div class="segment ui">
|
||||
<h4 class="ui header dividing">系統狀態</h4>
|
||||
<div class="ui divided list">
|
||||
<div class="item">
|
||||
<div class="ui horizontal label basic">API Key</div>
|
||||
<div class="description t-right" id="apikey"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="column eight wide">
|
||||
<div class="segment ui">
|
||||
<h4 class="ui header dividing">藍芽狀態</h4>
|
||||
<div class="ui divided list">
|
||||
<div class="item">
|
||||
<div class="ui horizontal label basic">啟用狀態</div>
|
||||
<div class="description t-right" id="en-ble">On</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="ui horizontal label basic">Mac Address</div>
|
||||
<div class="description t-right" id="ble-mac">00:11:22:33:44:55:66</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="ui horizontal label basic">ServiceUUID</div>
|
||||
<div class="description t-right" id="ble-service">37B2975E-1A4B-4975-9211-9D47142B8183</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="ui horizontal label basic">CharacteristicUUID</div>
|
||||
<div class="description t-right" id="ble-character">37B2975E-1A4B-4975-9211-9D47142B8183</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="column eight wide">
|
||||
<div class="segment ui">
|
||||
<h4 class="ui header dividing">印表機狀態</h4>
|
||||
<div class="ui divided list">
|
||||
<div class="item">
|
||||
<div class="ui horizontal label basic">連線狀態</div>
|
||||
<div class="description t-right" id="prt-con">Disconnected</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="ui horizontal label basic">連接埠</div>
|
||||
<div class="description t-right" id="prt-port">/dev/ttyUSB0</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="ui horizontal label basic">切紙前空行</div>
|
||||
<div class="description t-right" id="prt-feed">8</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="column eight wide">
|
||||
<div class="segment ui">
|
||||
<h4 class="ui header dividing">列印計數器</h4>
|
||||
<div class="ui divided list">
|
||||
<div class="item">
|
||||
<div class="ui horizontal label basic">列印次數</div>
|
||||
<div class="description t-right" id="prt-count">0</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="column eight wide">
|
||||
<div class="segment ui">
|
||||
<h4 class="ui header dividing">最後列印內容</h4>
|
||||
<div id="prt-last"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ui page dimmer" id="qr-dimmer">
|
||||
<div class="content">
|
||||
<div class="center" id="qr"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var setting = {
|
||||
mac: '',
|
||||
service: '',
|
||||
characteristic: ''
|
||||
}
|
||||
|
||||
var apikey = ''
|
||||
|
||||
var ws = new WebSocket('ws://' + location.host)
|
||||
ws.onmessage = msg => {
|
||||
// console.log(msg)
|
||||
let data = {}
|
||||
try {
|
||||
data = JSON.parse(msg.data)
|
||||
} catch (err) {
|
||||
return
|
||||
}
|
||||
|
||||
switch (data.type) {
|
||||
case 'status':
|
||||
console.log(data)
|
||||
let d = data.data
|
||||
$('#en-ble').text(d.ble.enable ? 'On' : 'Off')
|
||||
$('#ble-mac').text(d.ble.mac)
|
||||
$('#ble-service').text(d.ble.service)
|
||||
$('#ble-character').text(d.ble.characteristic)
|
||||
|
||||
setting.mac = d.ble.mac
|
||||
setting.service = d.ble.service
|
||||
setting.characteristic = d.ble.characteristic
|
||||
|
||||
apikey = d.secret
|
||||
$('#apikey').text(apikey)
|
||||
|
||||
$('#prt-con').text(d.printer.connect ? 'Connected' : 'Disconnected')
|
||||
$('#prt-port').text(d.printer.serial)
|
||||
$('#prt-feed').text(d.printer.feed)
|
||||
|
||||
$('#prt-count').text(d.pcount)
|
||||
|
||||
$('#prt-last').text(d.lastprint)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
$('#gen-ble-qr, #gen-api-qr').click(function () {
|
||||
let qrdiv = document.querySelector('div#qr')
|
||||
qrdiv.innerHTML = ''
|
||||
let qr = new QRCode(qrdiv, {
|
||||
text: this.id === 'gen-ble-qr' ? JSON.stringify(setting, null, 2) : JSON.stringify({apikey}, null, 2),
|
||||
colorDark: "#000000",
|
||||
colorLight: "#ffffff",
|
||||
correctLevel: QRCode.CorrectLevel.L
|
||||
})
|
||||
// qr.makeCode(JSON.stringify(setting, null, 2))
|
||||
$('#qr-dimmer').dimmer('show')
|
||||
})
|
||||
|
||||
</script>
|
|
@ -0,0 +1,2 @@
|
|||
</body>
|
||||
</html>
|
|
@ -0,0 +1,14 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title><%- title %></title>
|
||||
<link rel="stylesheet" href="/css/semantic.min.css">
|
||||
<link rel="stylesheet" href="/css/main.css">
|
||||
<script src="/js/jquery-3.2.1.min.js"></script>
|
||||
<script src="/js/semantic.min.js"></script>
|
||||
<script src="/js/qrcode.min.js"></script>
|
||||
</head>
|
||||
<body>
|
|
@ -0,0 +1,134 @@
|
|||
<%- include ../includes/header.ejs %>
|
||||
|
||||
<div class="ui container" style="padding-top: 20px;">
|
||||
<h1 class="ui header">系統安裝設定</h1>
|
||||
<form id="setup" class="ui form">
|
||||
<h3 class="ui dividing header">印表機設定</h3>
|
||||
<div class="field">
|
||||
<label for="tty">印表機連接埠</label>
|
||||
<select name="tty" id="tty">
|
||||
<option value="">選擇裝置</option>
|
||||
<% for(let i of ttys) { %>
|
||||
<option value="<%= i %>"><%= i %></option>
|
||||
<% } %>
|
||||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="feed">切紙前空行</label>
|
||||
<input type="text" name="feed" value="8" id="feed">
|
||||
</div>
|
||||
<div class="field">
|
||||
<button class="ui button mini" type="button" id="test-printer">測試印表機</button>
|
||||
</div>
|
||||
|
||||
<h3 class="ui dividing header">藍芽列印設定</h3>
|
||||
<div class="field">
|
||||
<div class="ui checkbox">
|
||||
<input type="checkbox" class="hidden" id="en-ble">
|
||||
<label for="en-ble">啟用藍芽</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3 class="ui dividing header">WebAPI設定</h3>
|
||||
<div class="field">
|
||||
<label for="api-secret">API密鑰</label>
|
||||
<input type="text" name="api-secret" placeholder="請輸入密鑰,空白不設定" id="api-secret">
|
||||
</div>
|
||||
|
||||
<div style="margin-top: 40px; text-align: right;">
|
||||
<button class="ui button blue" type="submit">送出設定</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="ui dimmer" id="loader">
|
||||
<div class="ui loader text">Rebooting...</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function getValue(obj){
|
||||
if(!obj || !('value' in obj)) return ''
|
||||
return obj.value
|
||||
}
|
||||
|
||||
$('#test-printer').click(function(){
|
||||
let json = {
|
||||
device: '',
|
||||
feed: 0
|
||||
}
|
||||
let dev = getValue(document.querySelector('select#tty'))
|
||||
let feed = getValue(document.querySelector('input#feed'))
|
||||
|
||||
if(!dev) return alert('請選擇印表機連接埠')
|
||||
if (!isFinite(feed) || feed < 0) return alert('切紙前空行請輸入整數')
|
||||
|
||||
json.device = dev
|
||||
json.feed = Math.floor(parseInt(feed))
|
||||
|
||||
$.ajax({
|
||||
url: '/install/printer_test',
|
||||
type: 'post',
|
||||
data: json,
|
||||
success: function(e){
|
||||
alert(e.msg || '資料傳送失敗')
|
||||
}
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
$('form#setup').submit(function(e) {
|
||||
e.preventDefault()
|
||||
|
||||
let json = {
|
||||
tty: '',
|
||||
feed: 0,
|
||||
ble: '',
|
||||
secret: ''
|
||||
}
|
||||
let dev = getValue(document.querySelector('select#tty'))
|
||||
let feed = getValue(document.querySelector('input#feed'))
|
||||
let elBle = document.querySelector('input#en-ble')
|
||||
let ble = ''
|
||||
if(elBle != null && 'checked' in elBle) ble = elBle.checked ? 'yes' : 'no'
|
||||
|
||||
if(!dev) return alert('請選擇印表機連接埠')
|
||||
if (!isFinite(feed) || feed < 0) return alert('切紙前空行請輸入整數')
|
||||
|
||||
let secret = getValue(document.querySelector('input#api-secret'))
|
||||
|
||||
json.tty = dev
|
||||
json.feed = Math.floor(parseInt(feed))
|
||||
json.ble = ble
|
||||
json.secret = secret
|
||||
|
||||
$.ajax({
|
||||
url: '/install/write_config',
|
||||
type: 'post',
|
||||
data: json,
|
||||
success: function(e){
|
||||
if(e.status != 1) {
|
||||
alert(e.msg || '寫入失敗')
|
||||
return
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: '/reboot_sys',
|
||||
type: 'put',
|
||||
success: function(){
|
||||
setTimeout(function(){
|
||||
location.replace('/')
|
||||
}, 30000)
|
||||
$('#loader').addClass('active')
|
||||
},
|
||||
error: function(){
|
||||
alert('系統重新啟動失敗')
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
return false
|
||||
})
|
||||
</script>
|
||||
|
||||
<%- include ../includes/footer.ejs %>
|
Loading…
Reference in New Issue