Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DFU: support choose zip file to upload #126

Merged
merged 1 commit into from
Jun 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 79 additions & 11 deletions pug/src/dfu.pug
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,23 @@ block content
option(value="ble") BLE (PC & Android)
option(value="usb") USB Serial (PC only)
.input-group-append: button.btn.btn-outline-secondary(@click="btnAdapterTips") #[i.fa.fa-fw.fa-question]
.form-group.letter-spacing-n1px.mb-3
label Tag Name:
select.form-control.form-control-sm(v-model="ss.tagName")
option(v-for="[k, v] of tagNames", :value="k") {{ v }}
button.btn.btn-block.btn-outline-primary.letter-spacing-n1px.mb-2(@click="btnUploadFirmware") #[i.fa.mr-1.fa-download] Upload Firmware
.card.shadow-sm.mb-2
h6.card-header #[i.fa.fa-fw.fa-github] From GitHub
.card-body.px-3.pt-3.pb-2.letter-spacing-n1px
.form-group.letter-spacing-n1px.mb-3
label Tag Name:
select.form-control.form-control-sm(v-model="ss.tagName")
option(v-for="[k, v] of tagNames", :value="k") {{ v }}
button.btn.btn-block.btn-outline-primary.letter-spacing-n1px.mb-2(@click="btnUploadRelease") #[i.fa.mr-1.fa-puzzle-piece] Upload Firmware
.card.shadow-sm.mb-2
h6.card-header #[i.fa.fa-fw.fa-file-archive-o] From Zip File
.card-body.px-3.pt-3.pb-2.letter-spacing-n1px
.form-group.letter-spacing-n1px.mb-3
label Choose firmware:
.custom-file.form-control-sm.letter-spacing-n1px
input.custom-file-input#imageSelected(type="file", v-model="imageSelected", ref="imageSelected", accept=".zip")
label.custom-file-label(for="imageSelected") {{ getFilename(imageSelected) || 'Choose firmware...' }}
button.btn.btn-block.btn-outline-primary.letter-spacing-n1px.mb-2(@click="btnUploadSelected") #[i.fa.mr-1.fa-puzzle-piece] Upload Firmware

block script
script.
Expand All @@ -58,6 +70,7 @@ block script
ss: {
tagName: '',
},
imageSelected: '',
manifest: {},
},
async mounted () {
Expand Down Expand Up @@ -109,7 +122,7 @@ block script
this.ss.tagName = _.first(this.tagNames)[0]
Swal.close()
},
async btnUploadFirmware () {
async btnUploadRelease () {
const { ultra } = this
const numFormater = new Intl.NumberFormat('en', { minimumFractionDigits: 1, maximumFractionDigits: 1 })
const showProgress = ({ func, offset, size, type }) => {
Expand All @@ -129,14 +142,22 @@ block script
return await dfuZip.getAppImage()
}))
this.showLoading({ text: 'Connect device...' })
await ultra.connect()
if (!ultra.isConnected()) await ultra.connect()
if (!ultra.isDfu()) {
const gitVersion = await ultra.cmdGetGitVersion()
if (!_.isNil(release.gitVersion) && release.gitVersion === gitVersion) {
const msg1 = `gitVersion(${gitVersion}) is the same, do you want to upload again?`
if (!await this.confirm(msg1, 'Yes', 'Cancel')) return
try {
const gitVersion = await ultra.cmdGetGitVersion()
if (!_.isNil(release.gitVersion) && release.gitVersion === gitVersion) {
const msg1 = `gitVersion(${gitVersion}) is the same, do you want to upload again?`
if (!await this.confirm(msg1, 'Yes', 'Cancel')) return
}
} catch (err) {
// 有可能是因為不支援 cmdGetGitVersion
ultra.emitter.emit('error', _.set(new Error(err.message), 'originalError', err))
}
await ultra.cmdDfuEnter()
await Swal.fire({ icon: 'info', text: 'Please reconnect your device!' })
if (!ultra.isConnected()) await ultra.connect()
if (!ultra.isDfu()) throw new Error('Device is not in DFU mode')
}
this.showLoading({ text: 'Uploading Firmware...' })
ultra.emitter.on('progress', showProgress)
Expand All @@ -158,6 +179,50 @@ block script
}
ultra.emitter.removeListener('progress', showProgress)
},
async btnUploadSelected () {
const { ultra } = this
const numFormater = new Intl.NumberFormat('en', { minimumFractionDigits: 1, maximumFractionDigits: 1 })
const showProgress = ({ func, offset, size, type }) => {
if (func !== 'dfuUploadObject' || type !== 2) return
const width = _.round(size > 0 ? offset / size * 100 : 0, 1)
const html = `<div class="d-flex flex-column"><div class="progress mb-2"><div class="progress-bar progress-bar-striped" role="progressbar" style="width: ${width}%"></div></div><div class="d-flex justify-content-between"><span>Uploading:</span><span>${numFormater.format(width)} %</span></div></div>`
this.showLoading({ html })
}
try {
this.showLoading({ text: 'Unzip firmware...' })
const dfuFile = this.$refs.imageSelected.files[0]
if (_.isNil(dfuFile)) throw new Error('Please browse a firmware file.')
const dfuZip = new DfuZip(new Buffer(await dfuFile.arrayBuffer()))
const image = await dfuZip.getAppImage()
const imageGitVersion = await dfuZip.getGitVersion()
this.showLoading({ text: 'Connect device...' })
if (!ultra.isConnected()) await ultra.connect()
if (!ultra.isDfu()) {
try {
const gitVersion = await ultra.cmdGetGitVersion()
if (!_.isNil(imageGitVersion) && imageGitVersion === gitVersion) {
const msg1 = `gitVersion(${gitVersion}) is the same, do you want to upload again?`
if (!await this.confirm(msg1, 'Yes', 'Cancel')) return
}
} catch (err) {
// 有可能是因為不支援 cmdGetGitVersion
ultra.emitter.emit('error', _.set(new Error(err.message), 'originalError', err))
}
await ultra.cmdDfuEnter()
await Swal.fire({ icon: 'info', text: 'Please reconnect your device!' })
if (!ultra.isConnected()) await ultra.connect()
if (!ultra.isDfu()) throw new Error('Device is not in DFU mode')
}
this.showLoading({ text: 'Uploading Firmware...' })
ultra.emitter.on('progress', showProgress)
await ultra.dfuUploadImage(image)
await Swal.fire({ icon: 'success', title: 'Upload Success' })
} catch (err) {
ultra.emitter.emit('error', err)
await Swal.fire({ icon: 'error', title: 'Upload Failed', text: err.message })
}
ultra.emitter.removeListener('progress', showProgress)
},
async sleep (t) {
await new Promise(resolve => setTimeout(resolve, t))
},
Expand All @@ -179,6 +244,9 @@ block script
Swal.fire(args).then(res => { isConfirmed = res.isConfirmed })
})
},
getFilename (str) {
return str.replaceAll(/.*[/\\]/g, '')
},
showLoading (opts = {}) {
opts = {
allowOutsideClick: false,
Expand Down
4 changes: 2 additions & 2 deletions pug/src/mfkey32.pug
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ block content
option(value="usb") USB Serial (PC only)
.input-group-append: button.btn.btn-outline-secondary(@click="btnAdapterTips") #[i.fa.fa-fw.fa-question]
.card.mb-3
.card-header.letter-spacing-n1px #[i.fa.fa-fw.fa-id-card] Scan UID / Emulate Tag
h6.card-header #[i.fa.fa-fw.fa-id-card] Scan UID / Emulate Tag
.card-body
.input-group.input-group-sm.mb-2
.input-group-prepend: span.input-group-text.justify-content-center Slot
Expand All @@ -58,7 +58,7 @@ block content
.col.px-1: button.btn.btn-block.btn-success.letter-spacing-n1px(@click="btnScanTag") #[i.fa.fa-fw.fa-sign-out] Scan UID
.col.px-1: button.btn.btn-block.btn-primary.letter-spacing-n1px(@click="btnEmulateTag") #[i.fa.fa-fw.fa-sign-in] Emulate
.card.mb-3
.card-header.letter-spacing-n1px #[i.fa.fa-fw.fa-lock] Key recover / Check key
h6.card-header #[i.fa.fa-fw.fa-lock] Key recover / Check key
.card-body
.input-group.input-group-sm.mb-2.was-validated
.input-group-prepend: span.input-group-text.justify-content-center Block
Expand Down
6 changes: 3 additions & 3 deletions src/ChameleonUltra.ts
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ export class ChameleonUltra {

/**
* Read a response from device.
* @param timeout - The timeout in milliseconds.
* @param args.timeout - The timeout in milliseconds.
* @internal
* @group Internal
*/
Expand Down Expand Up @@ -1759,8 +1759,8 @@ export class ChameleonUltra {
* @param opts.data - The data to be send. If `appendCrc` is `true`, the maximum length of data is `62`, otherwise is `64`.
* @param opts.dataBitLength - Number of bits to send. Useful for send partial byte. `dataBitLength` is incompatible with `appendCrc`.
* @param opts.keepRfField - Set `true` to keep the RF field active after sending.
* @param opts.readResponse - Default value is `true`. Set `false` to skip reading tag response.
* @param opts.timeout - Default value is `1000 ms`. Maximum timeout for reading tag response in ms while `readResponse` is `true`.
* @param opts.waitResponse - Default value is `true`. Set `false` to skip reading tag response.
* @param opts.timeout - Default value is `1000 ms`. Maximum timeout for reading tag response in ms while `waitResponse` is `true`.
* @returns The response from tag.
* @group Reader Related
*/
Expand Down
12 changes: 11 additions & 1 deletion src/plugin/Debug.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import _ from 'lodash'
import { type Buffer } from '@taichunmin/buffer'
import { type ChameleonPlugin, type PluginInstallContext as ChameleonCtx } from '../ChameleonUltra'
import createDebugger, { type Debugger } from 'debug'

let Buffer1: typeof Buffer

export default class Debug implements ChameleonPlugin {
filter?: DebugFilter
debugers = new Map<string, Debugger>()
filter?: DebugFilter
name = 'debug'

async install (context: ChameleonCtx): Promise<this> {
const { ultra } = context
if (_.isNil(Buffer1)) Buffer1 = context.Buffer
ultra.emitter.on('error', (err: Error) => {
const errJson = errToJson(err)
ultra.emitter.emit('debug', 'error', jsonStringify(errJson))
Expand Down Expand Up @@ -68,6 +72,7 @@ export function stringifyClone (obj: any): any {
if (preventCircular.has(val1)) return '[Circular]'
preventCircular.add(val1)
}
if (Buffer1?.isBuffer(val1)) return { type: 'Buffer', hex: val1.toString('hex') }
if (typeof val1 === 'bigint') return val1.toString()
if (val1 instanceof Error) return errToJson(val1)
if (val1 instanceof Map) return _.fromPairs([...val1.entries()])
Expand All @@ -90,3 +95,8 @@ export function stringifyReplacer (this: any, key: any, val: any): any {
export function jsonStringify (obj: object, space?: number): string {
return JSON.stringify(stringifyClone(obj), stringifyReplacer, space)
}

export function arrayBufferViewToHex (view: ArrayBufferView): string {
const u8 = new Uint8Array(view.buffer, view.byteOffset, view.byteLength)
return _.map(u8, b => `0${b.toString(16)}`.slice(-2)).join('')
}
7 changes: 7 additions & 0 deletions src/plugin/DfuZip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ export default class DfuZip {
async getAppImage (): Promise<DfuImage | null> {
return await this.getImage(['application'])
}

async getGitVersion (): Promise<string | null> {
const image = await this.getAppImage()
if (_.isNil(image)) return null
// eslint-disable-next-line no-control-regex
return image.body.toString('utf8').match(/\x00(v\d+(?:\.\d+)*[\w-]*)\x00/)?.[1] ?? null
}
}

;((globalThis as any ?? {}).ChameleonUltraJS ?? {}).DfuZip = DfuZip // eslint-disable-line @typescript-eslint/prefer-optional-chain
Expand Down
1 change: 0 additions & 1 deletion tsup.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ export default defineConfig((options): Options[] => [
minify: !options.watch,
entry: [
'src/Crypto1.ts',
'src/plugin/BufferMockAdapter.ts',
'src/plugin/Debug.ts',
'src/plugin/DfuZip.ts',
'src/plugin/SerialPortAdapter.ts',
Expand Down