refactor is now primary and only version

master
Henry Jameson 1 year ago
parent af814363d7
commit e178414c66
  1. 395
      index.js
  2. 257
      index2.js
  3. 191
      statuslogger.js
  4. 235
      statuslogger2.js

@ -1,8 +1,16 @@
import { NFC } from 'nfc-pcsc'
import fetch from 'node-fetch'
import fs from 'fs'
import { program } from 'commander'
import { Feedback } from './statuslogger.js'
import {
Feedback,
INSTRUCTIONS,
CARD_PRESENT,
GUEST_PLAYING,
REGISTR_ABORT,
RENEW_CARD,
THANKS_FOR_PLAYING,
} from './statuslogger.js'
import { YACE } from './yacardemu.js'
import { wmmt3 } from './statemachine.js'
import { ICCard } from './nfc.js'
program
.option('-i, --i2c-bus <num>', 'i2c bus to use for LCD display')
@ -14,8 +22,9 @@ program.parse();
const opts = program.opts()
const CARDS_LOCATION = opts.cardsLocation || '/home/dietpi/YACards/'
const saves = opts.cardsLocation || '/home/dietpi/YACards/'
const logger = Feedback({ bus: opts.i2cBus, noLCD: opts.noLCD })
if (opts.noLCD) {
logger.info('[EABU]: Skipping LCD initialization on user\'s request')
} else if (opts.i2cBus) {
@ -29,196 +38,220 @@ if (!(opts.emulatorPort > 0)) {
logger.error('[EABU]: Emulator port not configured!')
process.exit(1)
}
const port = opts.emulatorPort
logger.info('[EABU]: Starting up (if this hangs check if pcscd is running)')
const nfc = new NFC()
logger.info('[EABU]: Starting up')
const sm = wmmt3()
const status = {
ejectionStage: '',
card: null,
ejectionCount: 0,
newCardRegistration: false,
guestPlay: false,
readyCard: false,
countdown: 0,
newCard: false,
renewPossible: false,
countdownCallback: null
}
const countDown = (secs, callback) => {
status.countdown = secs
status.countdownCallback = callback
const func = () => {
console.debug(status.countdown)
logger.setCountdown(status.countdown)
if (status.countdown > 0) {
status.countdown--
status.countdownTimeoutId = setTimeout(func, 1000)
} else {
if (status.countdownCallback) {
status.countdownCallback()
status.countdownCallback = null
}
}
}
func()
}
logger.info('[EABU]: Starting YACE api')
const yace = YACE(port)
yace.once('error', () => setTimeout(() => {
yace.startPolling()
logger.error('[YACE]: Emulator unavailable, retrying in 1 sec')
}, 1000))
yace.startPolling()
expectCardStatus: true,
expectCardS: true,
const onInit = () => {
logger.info('[YACE]: Ready for new play.')
yace.once('shutter-opened', () => {
sm.shutterOpen()
})
}
logger.info('[EABU]: NFC Initialized')
nfc.on('error', err => {
logger.error('[PCSC]:', err);
});
nfc.on('reader', reader => {
logger.info('[PCSC]: Card reader found: ' + reader)
reader.on('card', card => {
logger.info(`[PCSC]: Card ${card.uid} scanned`)
if (!status.readyCard) {
logger.info(`[EABU] Not inserting as game isn't ready for it...`)
status.card = null
status.insertionConfirmed = false
status.countdown = 0
logger.setMgmtCard(!!status.card)
logger.setCountdown(status.countdown)
logger.setMessage('notReady')
logger.info('[EABU]: Initializing NFC (if this hangs check if pcscd is running)')
const nfc = ICCard(logger, saves)
nfc.alt.on('new-card', () => {
if (sm.cannot('new-card')) {
logger.warn('[YACE]: Cannot prepare new card when game\'s not ready for it!')
switch (sm.state) {
case 'playing':
logger.setMessage(CARD_PRESENT)
break
case 'guest-playing':
logger.setMessage(GUEST_PLAYING)
break
default:
logger.setMessage(INSTRUCTIONS)
}
}
})
if (status.card === null && !status.guestPlay) {
status.card = card.uid
status.insertionConfirmed = false
status.countdown = 5
status.newCard = !fs.existsSync(CARDS_LOCATION + card.uid + '.track_0')
logger.setMgmtCard(!!status.card)
logger.setMgmtCardId(status.card)
logger.info(`[YACE] Telling YACE to insert ${card.uid}`)
if (status.newCard) {
logger.info(`[EABU] New card!`)
status.newCardRegistration = true
status.countdown = 60 // seconds
logger.setMessage('newCardInstructions')
logger.setCountdown(status.countdown)
fetch(`http://127.0.0.1:${port}/api/v1/loadedCard?cardname=` + card.uid, { method: 'POST' })
} else {
logger.info(`[EABU] Existing card!`)
logger.info(`[EABU] Inserting...`)
fetch(`http://127.0.0.1:${port}/api/v1/insertedCard?cardname=` + card.uid, { method: 'POST' })
}
} else {
logger.info(`[EABU] Ignoring as we already have a card or GUEST is playing...`)
logger.setMessage('notReady')
nfc.alt.on('old-card', () => {
if (sm.cannot('old-card')) {
logger.warn('[YACE]: Cannot insert card when game\'s not ready for it!')
switch (sm.state) {
case 'playing':
logger.setMessage(CARD_PRESENT)
break
case 'guest-playing':
logger.setMessage(GUEST_PLAYING)
break
default:
logger.setMessage(INSTRUCTIONS)
}
}
})
onInit()
sm.observe('onIdle', onInit)
sm.observe('onLeaveState', () => {
status.countdown = 0
status.countdownCallback = null
clearTimeout(status.countdownTimeoutId)
yace.removeAllListeners()
nfc.removeAllListeners()
})
sm.observe('onEnterState', (state) => {
console.log(state.to)
logger.setState(state.to)
})
sm.observe('onReadyCard', () => {
logger.info('[YACE]: Shutter opened!')
// countDown(10, () => {
yace.once('shutter-closed', () => {
logger.info('[YACE]: No card scanned!')
sm.timeout()
})
nfc.once('new-card', (id) => {
yace.prepareCard(id)
logger.setCardId(id)
sm.newCard()
})
nfc.once('old-card', (id) => {
yace.insertCard(id)
logger.setCardId(id)
sm.oldCard()
})
reader.on('error', err => {
logger.error('[PCSC] ', err);
});
})
// GUEST PLAY
sm.observe('onGpWaitDispense', () => {
countDown(30, () => {
logger.info('[YACE]: No card dispensed, assuming real guest play.')
sm.timeout()
})
const pollYACE = () => {
if (status.card !== null) {
fetch(`http://127.0.0.1:${port}/api/v1/hasCard`)
.then(res => res.json())
.then(bool => {
if (status.countdown > 0) {
// counting down trying to confirm insertion / ejection
status.countdown--
logger.setEmuCard(bool)
switch(status.ejectionStage) {
case 'initial':
status.renewDispenseConfirmed = bool
if (bool) status.countdown = 0
default:
status.insertionConfirmed = bool
if (bool) status.countdown = 0
break;
}
logger.setCountdown(status.countdown)
setTimeout(pollYACE, 1000)
} else if (!status.insertionConfirmed) {
// couldn't confirm insertion
status.card = null
logger.setMgmtCard(false)
status.newCard = false
logger.setMessage('abort')
logger.error('[YACE] Could not confirm card presence with emulator!')
logger.error('[YACE] Telling YACE to load dummy just in case')
fetch(`http://127.0.0.1:${port}/api/v1/loadedCard?cardname=dummy`, { method: 'POST' })
setTimeout(pollYACE, 500)
} else if (status.insertionConfirmed){
// insertion confirmed! waiting for ejection
if (!bool && !status.renewPossible) {
// Card was ejected for the first time
// might be our card, might be present card
// "our" card _should_ be the last one (otherwise emulator's own setup
// wouldn't be working, would it?)
logger.setMessage('renew')
status.renewPossible = true
status.countdown = 100
status.ejectionStage = 'initial'
status.ejectionCount++
} else if (bool && status.renewPossible && status.renewDispenseConfirmed) {
// Card was ""INSERTED"" again while we were waiting after ejecting
// indicating that card was renewed, this might (???) happen multiple times
logger.info('[YACE] Card was dispensed (?) during ejection process, indicating a renew!')
logger.setMessage('renew_conf')
status.renewDispenseConfirmed = false
// status.card = null
// status.newCard = false
// status.renewPossible = false
} else if (!bool && status.renewPossible && !status.renewDispenseConfirmed){
// No new card was inserted after timeout, meaning it didn't dispense any new cards
// and renew process is finished and we can fall back to dummy
logger.setMessage('renew_ok')
status.ejectionStage = ''
status.card = null
status.newCard = false
status.renewPossible = false
status.renewDispenseConfirmed = false
logger.info('[YACE] Telling YACE to load dummy')
fetch(`http://127.0.0.1:${port}/api/v1/loadedCard?cardname=dummy`, { method: 'POST' })
} else {
}
setTimeout(pollYACE, 250)
}
})
.catch(err => {
logger.error('[EABU] Failure polling YACE', err)
logger.setEmuCard('error')
setTimeout(pollYACE, 1000)
})
} else {
fetch(`http://127.0.0.1:${port}/api/v1/hasCard`)
.then(res => res.json())
.then(bool => {
logger.setEmuCard(bool)
if (status.guestPlay) {
logger.info('[YACE] Emulator GUEST card present: ' + bool)
}
if (bool) {
status.guestPlay = true
logger.setMgmtCard('guest')
}
if (status.guestPlay && !bool) {
status.guestPlay = false
logger.setMgmtCard(false)
}
setTimeout(pollYACE, 100)
})
.catch(err => {
logger.error('[EABU] Failure polling YACE')
logger.error('[EABU] ' + err)
logger.setEmuCard('error')
setTimeout(pollYACE, 1000)
})
}
}
yace.once('card-dispensed', () => {
logger.info('[YACE]: Guest card dispensed')
sm.dispense()
})
})
const pollYACE2 = () => {
fetch(`http://127.0.0.1:${port}/api/v1/readyCard`)
.then(res => res.json())
.then(bool => {
status.readyCard = bool
logger.setEmuOpen(status.readyCard)
setTimeout(pollYACE2, 250)
})
.catch(err => {
logger.error('[EABU] Failure polling YACE (loop2)')
logger.error('[EABU] ' + err)
logger.setEmuOpen('error')
setTimeout(pollYACE2, 1000)
})
}
sm.observe('onGuestPlaying', () => {
logger.warn('[YACE]: Guest game in progress')
yace.once('card-ejected', () => {
logger.info('[YACE]: Card ejected, guest game over.')
logger.setMessage(THANKS_FOR_PLAYING)
sm.eject()
yace.prepareCard('dummy')
logger.setCardId('')
})
})
// REGISTERED CARD
sm.observe('onOcCheckRenewal', () => {
logger.info('[YACE]: Card inserted, waiting in case renewal is needed')
yace.once('card-ejected', () => {
logger.info('[YACE]: Card ejected, renewal detected.')
logger.setMessage(RENEW_CARD)
sm.eject()
})
countDown(20, () => {
logger.info('[YACE]: No card ejected, no renewal detected.')
sm.timeout()
})
})
sm.observe('onOcWaitDispense', () => {
logger.info('[YACE]: Present card ejected (and discarded ;_;), waiting for new card to be dispensed')
yace.once('card-dispensed', () => {
logger.info('[YACE]: Renewed card dispensed')
sm.dispense()
})
yace.once('card-ejected', () => {
logger.warn('[YACE]: Card ejected again, not sure what\'s going on, resetting...')
sm.goto('idle')
})
countDown(30, () => {
logger.warn('[YACE]: Error: not dispensed within 30 seconds, assuming illegal state and resetting')
sm.goto('idle')
})
})
// UNREGISTERED CARD
sm.observe('onNcWaitShutter', () => {
logger.info('[YACE]: Waiting for shutter to close')
yace.once('shutter-closed', () => {
sm.shutterClose()
})
})
sm.observe('onNcWaitDispense', () => {
logger.info('[YACE]: "No card" is selected, waiting for card purchase')
pollYACE()
pollYACE2()
yace.once('card-dispensed', () => {
logger.info('[YACE]: New card dispensed!')
sm.dispense()
})
countDown(30, () => {
logger.warn('[YACE]: Error: not dispensed within 30 seconds, assuming guest play')
logger.setMessage(REGISTR_ABORT)
sm.timeout()
})
})
// CARD GAME OVER
sm.observe('onPlaying', () => {
logger.info('[YACE]: Game in progress')
yace.once('card-ejected', () => {
logger.warn('[YACE]: Card ejected, game over.')
yace.prepareCard('dummy')
logger.setCardId('')
logger.setMessage(THANKS_FOR_PLAYING)
sm.eject()
})
})

@ -1,257 +0,0 @@
import { program } from 'commander'
import {
Feedback,
INSTRUCTIONS,
CARD_PRESENT,
GUEST_PLAYING,
REGISTR_ABORT,
RENEW_CARD,
THANKS_FOR_PLAYING,
} from './statuslogger2.js'
import { YACE } from './yacardemu.js'
import { wmmt3 } from './statemachine.js'
import { ICCard } from './nfc.js'
program
.option('-i, --i2c-bus <num>', 'i2c bus to use for LCD display')
.option('--noLCD', 'don\'t use LCD display, not recommended for production use')
.option('-e, --emulator-port <num>', 'port of the card emulator')
.option('-c, --cards-location <path>', 'path where card data will be saved')
program.parse();
const opts = program.opts()
const saves = opts.cardsLocation || '/home/dietpi/YACards/'
const logger = Feedback({ bus: opts.i2cBus, noLCD: opts.noLCD })
if (opts.noLCD) {
logger.info('[EABU]: Skipping LCD initialization on user\'s request')
} else if (opts.i2cBus) {
logger.initLCD()
} else {
logger.error('[EABU]: No i2c bus is specified, nor --noLCD is used, aborting')
process.exit(1)
}
if (!(opts.emulatorPort > 0)) {
logger.error('[EABU]: Emulator port not configured!')
process.exit(1)
}
const port = opts.emulatorPort
logger.info('[EABU]: Starting up')
const sm = wmmt3()
const status = {
card: null,
countdown: 0,
countdownCallback: null
}
const countDown = (secs, callback) => {
status.countdown = secs
status.countdownCallback = callback
const func = () => {
console.debug(status.countdown)
logger.setCountdown(status.countdown)
if (status.countdown > 0) {
status.countdown--
status.countdownTimeoutId = setTimeout(func, 1000)
} else {
if (status.countdownCallback) {
status.countdownCallback()
status.countdownCallback = null
}
}
}
func()
}
logger.info('[EABU]: Starting YACE api')
const yace = YACE(port)
yace.once('error', () => setTimeout(() => {
yace.startPolling()
logger.error('[YACE]: Emulator unavailable, retrying in 1 sec')
}, 1000))
yace.startPolling()
const onInit = () => {
logger.info('[YACE]: Ready for new play.')
yace.once('shutter-opened', () => {
sm.shutterOpen()
})
}
logger.info('[EABU]: Initializing NFC (if this hangs check if pcscd is running)')
const nfc = ICCard(logger, saves)
nfc.alt.on('new-card', () => {
if (sm.cannot('new-card')) {
logger.warn('[YACE]: Cannot prepare new card when game\'s not ready for it!')
switch (sm.state) {
case 'playing':
logger.setMessage(CARD_PRESENT)
break
case 'guest-playing':
logger.setMessage(GUEST_PLAYING)
break
default:
logger.setMessage(INSTRUCTIONS)
}
}
})
nfc.alt.on('old-card', () => {
if (sm.cannot('old-card')) {
logger.warn('[YACE]: Cannot insert card when game\'s not ready for it!')
switch (sm.state) {
case 'playing':
logger.setMessage(CARD_PRESENT)
break
case 'guest-playing':
logger.setMessage(GUEST_PLAYING)
break
default:
logger.setMessage(INSTRUCTIONS)
}
}
})
onInit()
sm.observe('onIdle', onInit)
sm.observe('onLeaveState', () => {
status.countdown = 0
status.countdownCallback = null
clearTimeout(status.countdownTimeoutId)
yace.removeAllListeners()
nfc.removeAllListeners()
})
sm.observe('onEnterState', (state) => {
console.log(state.to)
logger.setState(state.to)
})
sm.observe('onReadyCard', () => {
logger.info('[YACE]: Shutter opened!')
// countDown(10, () => {
yace.once('shutter-closed', () => {
logger.info('[YACE]: No card scanned!')
sm.timeout()
})
nfc.once('new-card', (id) => {
yace.prepareCard(id)
logger.setCardId(id)
sm.newCard()
})
nfc.once('old-card', (id) => {
yace.insertCard(id)
logger.setCardId(id)
sm.oldCard()
})
})
// GUEST PLAY
sm.observe('onGpWaitDispense', () => {
countDown(30, () => {
logger.info('[YACE]: No card dispensed, assuming real guest play.')
sm.timeout()
})
yace.once('card-dispensed', () => {
logger.info('[YACE]: Guest card dispensed')
sm.dispense()
})
})
sm.observe('onGuestPlaying', () => {
logger.warn('[YACE]: Guest game in progress')
yace.once('card-ejected', () => {
logger.info('[YACE]: Card ejected, guest game over.')
logger.setMessage(THANKS_FOR_PLAYING)
sm.eject()
yace.prepareCard('dummy')
logger.setCardId('')
})
})
// REGISTERED CARD
sm.observe('onOcCheckRenewal', () => {
logger.info('[YACE]: Card inserted, waiting in case renewal is needed')
yace.once('card-ejected', () => {
logger.info('[YACE]: Card ejected, renewal detected.')
logger.setMessage(RENEW_CARD)
sm.eject()
})
countDown(20, () => {
logger.info('[YACE]: No card ejected, no renewal detected.')
sm.timeout()
})
})
sm.observe('onOcWaitDispense', () => {
logger.info('[YACE]: Present card ejected (and discarded ;_;), waiting for new card to be dispensed')
yace.once('card-dispensed', () => {
logger.info('[YACE]: Renewed card dispensed')
sm.dispense()
})
yace.once('card-ejected', () => {
logger.warn('[YACE]: Card ejected again, not sure what\'s going on, resetting...')
sm.goto('idle')
})
countDown(30, () => {
logger.warn('[YACE]: Error: not dispensed within 30 seconds, assuming illegal state and resetting')
sm.goto('idle')
})
})
// UNREGISTERED CARD
sm.observe('onNcWaitShutter', () => {
logger.info('[YACE]: Waiting for shutter to close')
yace.once('shutter-closed', () => {
sm.shutterClose()
})
})
sm.observe('onNcWaitDispense', () => {
logger.info('[YACE]: "No card" is selected, waiting for card purchase')
yace.once('card-dispensed', () => {
logger.info('[YACE]: New card dispensed!')
sm.dispense()
})
countDown(30, () => {
logger.warn('[YACE]: Error: not dispensed within 30 seconds, assuming guest play')
logger.setMessage(REGISTR_ABORT)
sm.timeout()
})
})
// CARD GAME OVER
sm.observe('onPlaying', () => {
logger.info('[YACE]: Game in progress')
yace.once('card-ejected', () => {
logger.warn('[YACE]: Card ejected, game over.')
yace.prepareCard('dummy')
logger.setCardId('')
logger.setMessage(THANKS_FOR_PLAYING)
sm.eject()
})
})

@ -15,82 +15,88 @@ const SPIN_CHARS = [
const SPINNER = SPIN_CHARS.map((c, i) => i)
const STATUS_LINE = [
'Status: _______'
'Status: ______'
]
const IDLE = [
'Use your IC card to ',
'play! Tap the card ',
'for instructions. ',
]
const WAITING_REGISTRATION = [
'Select "no card" and',
'then select purchace',
'new card. ',
]
const GAME_IN_PROGRESS = [
'Game in progress! ',
'Card id: ___________',
'GLHF! ',
]
const GUEST_IN_PROGRESS = [
'Guest game is a go! ',
'Buy a Nesica or aime',
'card later maybe? ',
]
const NEW_CARD_INSTRUCTIONS = [
'Run new game, select',
'"no card", and then ',
'"purchase new card".',
]
const states = {
idle: [
'Waiting for new game',
'Scan the card for ',
'instructions. ',
],
'ready-card': [
' ',
'Scan your card now!!',
' ',
],
'nc-wait-shutter': [
'New card detected! ',
'Please select the ' ,
'"No card" option ',
],
'nc-wait-dispense': [
'New card detected! ',
'Waiting for game to ' ,
'dispense a new card ',
],
'playing': [
'Game in progress, ',
'GLHF! Card id: ',
'____________________',
],
'gp-wait-dispense': [
'No card? Ask owner ',
'for one! Game data ',
'will be lost! '
],
'guest-playing': [
'No card? Ask owner ',
'for one! Game data ',
'will be lost! '
],
'oc-check-renewal': [
'Welcome back! ',
'Checking if card ' ,
'needs renewal... ',
],
'oc-wait-dispense': [
'Card renewed! ',
'Waiting for game to ' ,
'dispense a new card ',
]
}
const NEW_CARD_INSTRUCTIONS2 = [
'Start new game, then',
'select "no card" and',
'then "purchase" one.',
export const INSTRUCTIONS = [
'1) Step on the gas ',
'2) Scan your card ',
'3) (Register card) ',
]
const CARD_PRESENT = [
export const CARD_PRESENT = [
'Already scanned a ',
'card, please wait ',
'until new game. ',
]
const GUEST_PLAYING = [
export const GUEST_PLAYING = [
'Guest is currently ',
'playing, please wait',
'until new game. ',
]
const REGISTR_ABORT = [
export const REGISTR_ABORT = [
'Card registration is',
'aborted - time run ',
'out. Try again later',
]
const RENEW_CARD = [
'Waiting in case card',
'needs to be renewed.',
' ',
export const RENEW_CARD = [
'Card renewed! ',
'Present or discarded',
'card data is lost :(',
]
const RENEW_CARD_CONF = [
'New card dispensed! ',
'Card is being ',
'renewed! ',
]
const RENEW_CARD_OK = [
'Card is renewed! ',
'At least i think it ',
'is... t. hj',
]
const THANKS_FOR_PLAYING = [
export const THANKS_FOR_PLAYING = [
'Thanks for playing! ',
'Hope you had a good ',
'time. t. hj',
@ -102,92 +108,58 @@ export const Feedback = ({ bus, noLCD }) => {
spinnerIndex: 0,
spinner: SPINNER[0],
mgmtCard: false,
state: 'idle',
emuCard: false,
emuOpen: false,
playing: false,
cardId: '',
messageCountdown: 0,
messageType: null,
message: null,
countdown: 0,
}
const quipSelect = () => {
if (status.messageCountdown > 0) {
switch (status.messageType) {
case 'cardPresent':
if (status.mgmgtCard === 'guest') {
return GUEST_PLAYING
} else {
return CARD_PRESENT
}
case 'notReady':
return NEW_CARD_INSTRUCTIONS2
case 'abort':
return REGISTR_ABORT
case 'newCardInstructions':
return NEW_CARD_INSTRUCTIONS
case 'thanks':
return THANKS_FOR_PLAYING
case 'renew':
return RENEW_CARD
case 'renew_conf':
return RENEW_CARD_CONF
case 'renew_ok':
return RENEW_CARD_OK
default:
return [
'--------------------',
'ERROR: INVALID MSG!!',
'--------------------',
]
}
return status.message || [
'All your base ',
' Are belong to us!!',
' (invalid message) ',
]
} else {
if (!status.mgmtCard) {
return IDLE
} else {
if (status.mgmtCard && !status.emuCard) {
return WAITING_REGISTRATION
} else if (status.mgmtCard === 'guest') {
return GUEST_IN_PROGRESS
} else {
return GAME_IN_PROGRESS.map(x => x.replace('___________', status.cardId))
}
const statusQuip = states[status.state]
if (status.state === 'playing') {
statusQuip[2] = status.cardId.padEnd(20, ' ')
}
return statusQuip
}
}
const statusLineGenerator = () => {
console.log(status.countdown)
let countdownI = status.countdown > 0
? `${status.countdown}`.padStart(3, ' ')
: ' '
let mgmtCardI = '-'
if (status.mgmtCard === true) mgmtCardI = 'P'
if (status.mgmtCard === 'guest') mgmtCardI = 'G'
let emuCardI = status.emuCard ? 'C' : '_'
let emuOpenI = status.emuOpen ? 'O' : '#'
if (status.emuCard === 'error') {
emuCardI = 'E'
emuOpenI = 'E'
}
return countdownI + mgmtCardI + emuCardI + emuOpenI + status.spinner
return countdownI + emuCardI + emuOpenI + status.spinner
}
const lineGen = () => {
return STATUS_LINE[0].replace('_______', statusLineGenerator())
return STATUS_LINE[0].replace('______', statusLineGenerator())
}
const bufferGen = () => [ lineGen(), ...quipSelect() ]
return {
setMgmtCard(mgmtCard) {
status.mgmtCard = mgmtCard
},
setMgmtCardId(id) {
status.cardId = id
setState(state) {
status.state = state
},
setEmuCard(emuCard) {
status.emuCard = emuCard
@ -195,13 +167,16 @@ export const Feedback = ({ bus, noLCD }) => {
setEmuOpen(emuOpen) {
status.emuOpen = emuOpen
},
setMessage(messageType) {
status.messageType = messageType
status.messageCountdown = 20 * 2
setMessage(message) {
status.message = message
status.messageCountdown = 10 * 2
},
setCountdown(countdown) {
status.countdown = countdown
},
setCardId(cardId) {
status.cardId = cardId
},
warn(...string) {
console.warn(wrn('[!]') + ' ' + string.join(' '))
},

@ -1,235 +0,0 @@
import chalk from 'chalk'
import LCD from 'raspberrypi-liquid-crystal'
let lcd
const err = chalk.bold.redBright
const inf = chalk.blueBright
const wrn = chalk.yellowBright
const SPIN_CHARS = [
[0x14,0x1c,0x15,0x1,0x6,0x0,0x00,0x0],
[0x14,0x1c,0x15,0x1,0x6,0x0,0x10,0x0],
[0x14,0x1c,0x15,0x1,0x6,0x0,0x14,0x0],
[0x14,0x1c,0x15,0x1,0x6,0x0,0x15,0x0]
]
const SPINNER = SPIN_CHARS.map((c, i) => i)
const STATUS_LINE = [
'Status: ______'
]
const states = {
idle: [
'Waiting for new game',
'Scan the card for ',
'instructions. ',
],
'ready-card': [
' ',
'Scan your card now!!',
' ',
],
'nc-wait-shutter': [
'New card detected! ',
'Please select the ' ,
'"No card" option ',
],
'nc-wait-dispense': [
'New card detected! ',
'Waiting for game to ' ,
'dispense a new card ',
],
'playing': [
'Game in progress, ',
'GLHF! Card id: ',
'____________________',
],
'gp-wait-dispense': [
'No card? Ask owner ',
'for one! Game data ',
'will be lost! '
],
'guest-playing': [
'No card? Ask owner ',
'for one! Game data ',
'will be lost! '
],
'oc-check-renewal': [
'Welcome back! ',
'Checking if card ' ,
'needs renewal... ',
],
'oc-wait-dispense': [
'Card renewed! ',
'Waiting for game to ' ,
'dispense a new card ',
]
}
export const INSTRUCTIONS = [
'1) Step on the gas ',
'2) Scan your card ',
'3) (Register card) ',
]
export const CARD_PRESENT = [
'Already scanned a ',
'card, please wait ',
'until new game. ',
]
export const GUEST_PLAYING = [
'Guest is currently ',
'playing, please wait',
'until new game. ',
]
export const REGISTR_ABORT = [
'Card registration is',
'aborted - time run ',
'out. Try again later',
]
export const RENEW_CARD = [
'Card renewed! ',
'Present or discarded',
'card data is lost :(',
]
export const THANKS_FOR_PLAYING = [
'Thanks for playing! ',
'Hope you had a good ',
'time. t. hj',
]
export const Feedback = ({ bus, noLCD }) => {
const status = {
hasLCD: false,
spinnerIndex: 0,
spinner: SPINNER[0],
state: 'idle',
emuCard: false,
emuOpen: false,
cardId: '',
messageCountdown: 0,
message: null,
countdown: 0,
}
const quipSelect = () => {
if (status.messageCountdown > 0) {
return status.message || [
'All your base ',
' Are belong to us!!',
' (invalid message) ',
]
} else {
const statusQuip = states[status.state]
if (status.state === 'playing') {
statusQuip[2] = status.cardId.padEnd(20, ' ')
}
return statusQuip
}
}
const statusLineGenerator = () => {
console.log(status.countdown)
let countdownI = status.countdown > 0
? `${status.countdown}`.padStart(3, ' ')
: ' '
let emuCardI = status.emuCard ? 'C' : '_'
let emuOpenI = status.emuOpen ? 'O' : '#'
if (status.emuCard === 'error') {
emuCardI = 'E'
emuOpenI = 'E'
}
return countdownI + emuCardI + emuOpenI + status.spinner
}
const lineGen = () => {
return STATUS_LINE[0].replace('______', statusLineGenerator())
}
const bufferGen = () => [ lineGen(), ...quipSelect() ]
return {
setState(state) {
status.state = state
},
setEmuCard(emuCard) {
status.emuCard = emuCard
},
setEmuOpen(emuOpen) {
status.emuOpen = emuOpen
},
setMessage(message) {
status.message = message
status.messageCountdown = 10 * 2
},
setCountdown(countdown) {
status.countdown = countdown
},
setCardId(cardId) {
status.cardId = cardId
},
warn(...string) {
console.warn(wrn('[!]') + ' ' + string.join(' '))
},
info(...string) {
console.info(inf('[ ]') + ' ' + string.join(' '))
},
error(...string) {
console.error(err('[!]') + ' ' + string.join(' '))
},
initLCD() {
this.info('Initializing LCD...')
const loop = () => {
const buffer = bufferGen()
this.advanceSpinner()
//lcd.clearSync()
buffer.forEach((line, i) => lcd.printLineSync(i, line.substring(0, 20)))
if (status.messageCountdown > 0) {
status.messageCountdown--
if (status.messageCountdown === 0) {
status.messageType = null
}
}
setTimeout(loop, 100)
}
lcd = new LCD(Number(bus), 0x27, 20, 4);
lcd
.begin()
.then(() => {
status.hasLCD = true
this.info('LCD Initialized!')
lcd.clearSync()
lcd.printLineSync(0, 'Wangan Midnight ')
lcd.printLineSync(1, ' Maximum Tune 3DX+')
lcd.printLineSync(2, ' IC card support ')
lcd.printLineSync(3, ' by HJ (and GXTX)')
SPIN_CHARS.forEach((c, i) => {
lcd.createCharSync(i, c)
})
setTimeout(loop, 3000)
})
.catch((e) => {
status.hasLCD = false
this.warn('Error initializing LCD: ', e)
})
},
advanceSpinner() {
if (status.spinnerIndex >= SPINNER.length) status.spinnerIndex = 0
status.spinner = LCD.getChar(status.spinnerIndex++)
}
}
}
Loading…
Cancel
Save