2020-01-14 19:45:41 +00:00
|
|
|
#!/usr/bin/env ruby
|
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
# SPDX-License-Identifier: MIT OR Apache-2.0
|
|
|
|
#
|
2021-01-01 10:28:32 +00:00
|
|
|
# Copyright (c) 2020-2021 Andre Richter <andre.o.richter@gmail.com>
|
2020-01-14 19:45:41 +00:00
|
|
|
|
2020-11-08 22:37:17 +00:00
|
|
|
require_relative 'miniterm'
|
2020-01-14 19:45:41 +00:00
|
|
|
require 'ruby-progressbar'
|
2020-01-19 20:34:37 +00:00
|
|
|
require_relative 'minipush/progressbar_patch'
|
2020-11-08 22:37:17 +00:00
|
|
|
require 'timeout'
|
2020-01-14 19:45:41 +00:00
|
|
|
|
|
|
|
class ProtocolError < StandardError; end
|
|
|
|
|
|
|
|
# The main class
|
2020-11-08 22:37:17 +00:00
|
|
|
class MiniPush < MiniTerm
|
2020-03-06 22:56:38 +00:00
|
|
|
def initialize(serial_name, binary_image_path)
|
2020-11-08 22:37:17 +00:00
|
|
|
super(serial_name)
|
|
|
|
|
2020-12-26 22:59:16 +00:00
|
|
|
@name_short = 'MP' # override
|
2020-03-06 22:56:38 +00:00
|
|
|
@binary_image_path = binary_image_path
|
|
|
|
@binary_size = nil
|
|
|
|
@binary_image = nil
|
2020-01-14 19:45:41 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
2021-01-04 13:37:17 +00:00
|
|
|
# The three characters signaling the request token form the consecutive sequence "\x03\x03\x03".
|
2020-01-14 19:45:41 +00:00
|
|
|
def wait_for_binary_request
|
2020-12-26 22:59:16 +00:00
|
|
|
puts "[#{@name_short}] 🔌 Please power the target now"
|
2020-01-14 19:45:41 +00:00
|
|
|
|
2020-12-26 22:59:16 +00:00
|
|
|
# Timeout for the request token starts after the first sign of life was received.
|
|
|
|
received = @target_serial.readpartial(4096)
|
|
|
|
Timeout.timeout(10) do
|
2021-01-04 13:37:17 +00:00
|
|
|
count = 0
|
|
|
|
|
2020-12-26 22:59:16 +00:00
|
|
|
loop do
|
|
|
|
raise ProtocolError if received.nil?
|
2020-01-14 19:45:41 +00:00
|
|
|
|
2021-01-04 13:37:17 +00:00
|
|
|
received.chars.each do |c|
|
|
|
|
if c == "\u{3}"
|
|
|
|
count += 1
|
|
|
|
return true if count == 3
|
|
|
|
else
|
|
|
|
# A normal character resets token counting.
|
|
|
|
count = 0
|
2020-01-14 19:45:41 +00:00
|
|
|
|
2021-01-04 13:37:17 +00:00
|
|
|
print c
|
|
|
|
end
|
|
|
|
end
|
2020-12-26 22:59:16 +00:00
|
|
|
|
|
|
|
received = @target_serial.readpartial(4096)
|
2020-01-14 19:45:41 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-03-06 22:56:38 +00:00
|
|
|
def load_binary
|
|
|
|
@binary_size = File.size(@binary_image_path)
|
|
|
|
@binary_image = File.binread(@binary_image_path)
|
|
|
|
end
|
|
|
|
|
2020-01-14 19:45:41 +00:00
|
|
|
def send_size
|
|
|
|
@target_serial.print([@binary_size].pack('L<'))
|
|
|
|
raise ProtocolError if @target_serial.read(2) != 'OK'
|
|
|
|
end
|
|
|
|
|
|
|
|
def send_binary
|
|
|
|
pb = ProgressBar.create(
|
|
|
|
total: @binary_size,
|
2020-11-08 22:37:17 +00:00
|
|
|
format: "[#{@name_short}] ⏩ Pushing %k KiB %b🦀%i %p%% %r KiB/s %a",
|
2020-01-14 19:45:41 +00:00
|
|
|
rate_scale: ->(rate) { rate / 1024 },
|
|
|
|
length: 92
|
|
|
|
)
|
|
|
|
|
|
|
|
# Send in 512 byte chunks.
|
|
|
|
while pb.progress < pb.total
|
|
|
|
part = @binary_image.slice(pb.progress, 512)
|
|
|
|
pb.progress += @target_serial.write(part)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-12-26 22:59:16 +00:00
|
|
|
# override
|
|
|
|
def handle_reconnect
|
2020-01-14 19:45:41 +00:00
|
|
|
connetion_reset
|
|
|
|
|
|
|
|
puts
|
2020-11-08 22:37:17 +00:00
|
|
|
puts "[#{@name_short}] ⚡ " \
|
2020-12-26 22:59:16 +00:00
|
|
|
"#{'Connection or protocol Error: '.light_red}" \
|
|
|
|
"#{'Remove power and USB serial. Reinsert serial first, then power'.light_red}"
|
2020-01-14 19:45:41 +00:00
|
|
|
sleep(1) while serial_connected?
|
|
|
|
end
|
|
|
|
|
|
|
|
public
|
|
|
|
|
2020-12-26 22:59:16 +00:00
|
|
|
# override
|
2020-01-14 19:45:41 +00:00
|
|
|
def run
|
|
|
|
open_serial
|
|
|
|
wait_for_binary_request
|
2020-03-06 22:56:38 +00:00
|
|
|
load_binary
|
2020-01-14 19:45:41 +00:00
|
|
|
send_size
|
|
|
|
send_binary
|
|
|
|
terminal
|
2020-12-26 22:59:16 +00:00
|
|
|
rescue ConnectionError, EOFError, Errno::EIO, ProtocolError, Timeout::Error
|
2020-01-14 19:45:41 +00:00
|
|
|
handle_reconnect
|
|
|
|
retry
|
|
|
|
rescue StandardError => e
|
|
|
|
handle_unexpected(e)
|
|
|
|
ensure
|
|
|
|
connetion_reset
|
|
|
|
puts
|
2020-11-08 22:37:17 +00:00
|
|
|
puts "[#{@name_short}] Bye 👋"
|
2020-01-14 19:45:41 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-03-12 22:44:10 +00:00
|
|
|
puts
|
2020-01-14 19:45:41 +00:00
|
|
|
puts 'Minipush 1.0'.cyan
|
|
|
|
puts
|
|
|
|
|
|
|
|
# CTRL + C handler. Only here to suppress Ruby's default exception print.
|
|
|
|
trap('INT') do
|
|
|
|
# The `ensure` block from `MiniPush::run` will run after exit, restoring console state.
|
|
|
|
exit
|
|
|
|
end
|
|
|
|
|
|
|
|
MiniPush.new(ARGV[0], ARGV[1]).run
|