Skip to content

Commit

Permalink
Merge pull request #176 from synthead/add-protocol-7-phrase-model-class
Browse files Browse the repository at this point in the history
Add protocol 7 phrase model class
  • Loading branch information
synthead authored Nov 27, 2022
2 parents 1fb49a6 + 866375b commit 657f6d8
Show file tree
Hide file tree
Showing 6 changed files with 213 additions and 3 deletions.
1 change: 1 addition & 0 deletions lib/timex_datalink_client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
require "timex_datalink_client/protocol_7/eeprom"
require "timex_datalink_client/protocol_7/eeprom/activity"
require "timex_datalink_client/protocol_7/eeprom/phone_number"
require "timex_datalink_client/protocol_7/eeprom/phrase"
require "timex_datalink_client/protocol_7/end"
require "timex_datalink_client/protocol_7/start"
require "timex_datalink_client/protocol_7/sync"
Expand Down
7 changes: 5 additions & 2 deletions lib/timex_datalink_client/protocol_7/eeprom.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,18 @@ class Eeprom

CPACKET_DATA_LENGTH = 32

attr_accessor :activities, :phone_numbers
attr_accessor :activities, :phone_numbers, :phrases

# Create an Eeprom instance.
#
# @param activities [Array<Activity>, nil] Activities to be added to EEPROM data.
# @param phone_numbers [Array<PhoneNumber>, nil] Phone numbers to be added to EEPROM data.
# @param phrases [Array<Phrase>, nil] Phrases to be added to EEPROM data.
# @return [Eeprom] Eeprom instance.
def initialize(activities: nil, phone_numbers: nil)
def initialize(activities: nil, phone_numbers: nil, phrases: nil)
@activities = activities
@phone_numbers = phone_numbers
@phrases = phrases
end

# Compile packets for EEPROM data.
Expand Down Expand Up @@ -57,6 +59,7 @@ def all_packets
[].tap do |packets|
packets.concat(Activity.packets(activities)) if activities
packets.concat(PhoneNumber.packets(phone_numbers)) if phone_numbers
packets.concat(Phrase.packets(phrases)) if phrases
end
end
end
Expand Down
85 changes: 85 additions & 0 deletions lib/timex_datalink_client/protocol_7/eeprom/phrase.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# frozen_string_literal: true

require "timex_datalink_client/helpers/four_byte_formatter"

class TimexDatalinkClient
class Protocol7
class Eeprom
class Phrase
include Helpers::FourByteFormatter

METADATA_BYTES_BASE = 0x0b
METADATA_BYTES_SIZE = 5

HEADER_BOOL_TRUE_VALUE = 0x1a
HEADER_BOOL_FALSE_VALUE = 0x00
HEADER_ONE_BYTE_VALUE_BASE = 0x0b
HEADER_TWO_BYTE_VALUES_BASE = [0x1a, 0x24, 0x2e, 0x38, 0x47, 0x56, 0x60, 0x6a, 0x6f, 0x79, 0x83]
HEADER_TWO_BYTE_LOOP_BASE = 0x8d

HEADER_FOOTER = [
0xf0, 0x53, 0xfd, 0x4d, 0x03, 0xb0, 0x8d, 0xfe, 0x00, 0x00, 0xf0, 0x53, 0xfd, 0x4d, 0x03, 0xb0, 0x7b, 0xfe,
0x00, 0x00, 0xf0, 0xfb, 0x63, 0x39, 0x3c, 0xc0, 0xfe, 0x00, 0x00, 0x00, 0xf0, 0xfb, 0x61, 0x39, 0x3c, 0x70,
0x94, 0xfd, 0x4b, 0x03, 0x7c, 0x44, 0x27, 0xfe, 0x00, 0xd3, 0xfb, 0xae, 0x30, 0x29, 0x33, 0x3c, 0xfb, 0x30,
0x20, 0x07, 0x3c, 0x39, 0x24, 0xfe, 0xf1, 0xfb, 0x53, 0x03, 0xae, 0x9c, 0xe6, 0x8e, 0xfe, 0x00, 0xc1, 0x61,
0x39, 0x3c, 0x44, 0xcc, 0xfd, 0x4b, 0xfe, 0x00, 0xff, 0xfb, 0x61, 0x3e, 0xfe, 0x41, 0xcb, 0x39, 0x3c, 0x44,
0xcc, 0xfd, 0x4b, 0xfe, 0x00, 0x37, 0x39, 0x5a, 0xae, 0xfd, 0x1f, 0x4b, 0x8e, 0x81, 0xfe, 0x3f, 0x39, 0x5a,
0x1c, 0x81, 0xc0, 0xff, 0x00, 0x00, 0x00
]

PACKETS_TERMINATOR = 0x05

# Compile data for all phrases.
#
# @param phrases [Array<Phrase>] Phrases to compile data for.
# @return [Array] Compiled data of all phrases.
def self.packets(phrases)
header(phrases) + formatted_phrases(phrases) + [PACKETS_TERMINATOR]
end

private_class_method def self.header(phrases)
bool_value = phrases.empty? ? HEADER_BOOL_FALSE_VALUE : HEADER_BOOL_TRUE_VALUE

one_byte_value = HEADER_ONE_BYTE_VALUE_BASE + phrases.count

two_byte_values = HEADER_TWO_BYTE_VALUES_BASE.flat_map do |value_base|
value = value_base + phrases.count * 2
value.divmod(256).reverse
end

loop_values = phrases.each_index.flat_map do |phrase_index|
value = HEADER_TWO_BYTE_LOOP_BASE + phrases.count * 2 + phrase_index * 5
value.divmod(256).reverse
end

[
one_byte_value,
0,
bool_value,
0,
two_byte_values,
loop_values,
HEADER_FOOTER
].flatten
end

private_class_method def self.formatted_phrases(phrases)
return [] if phrases.empty?

phrases_bytes = phrases.map(&:phrase)
phrases.first.four_byte_format_for(phrases_bytes)
end

attr_accessor :phrase

# Create a Phrase instance.
#
# @param phrase [Array<Integer>] Phrase for phrase.
# @return [Phrase] Phrase instance.
def initialize(phrase:)
@phrase = phrase
end
end
end
end
end
77 changes: 77 additions & 0 deletions spec/lib/timex_datalink_client/protocol_7/eeprom/phrase_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# frozen_string_literal: true

require "spec_helper"

describe TimexDatalinkClient::Protocol7::Eeprom::Phrase do
let(:phrase) { [0xb1] }
let(:phrase_instance) { described_class.new(phrase: phrase) }

describe ".packets" do
let(:phrases) { [phrase_instance] }

subject(:packets) { described_class.packets(phrases) }

it do
should eq [
0x0c, 0x00, 0x1a, 0x00, 0x1c, 0x00, 0x26, 0x00, 0x30, 0x00, 0x3a, 0x00, 0x49, 0x00, 0x58, 0x00, 0x62, 0x00,
0x6c, 0x00, 0x71, 0x00, 0x7b, 0x00, 0x85, 0x00, 0x8f, 0x00, 0xf0, 0x53, 0xfd, 0x4d, 0x03, 0xb0, 0x8d, 0xfe,
0x00, 0x00, 0xf0, 0x53, 0xfd, 0x4d, 0x03, 0xb0, 0x7b, 0xfe, 0x00, 0x00, 0xf0, 0xfb, 0x63, 0x39, 0x3c, 0xc0,
0xfe, 0x00, 0x00, 0x00, 0xf0, 0xfb, 0x61, 0x39, 0x3c, 0x70, 0x94, 0xfd, 0x4b, 0x03, 0x7c, 0x44, 0x27, 0xfe,
0x00, 0xd3, 0xfb, 0xae, 0x30, 0x29, 0x33, 0x3c, 0xfb, 0x30, 0x20, 0x07, 0x3c, 0x39, 0x24, 0xfe, 0xf1, 0xfb,
0x53, 0x03, 0xae, 0x9c, 0xe6, 0x8e, 0xfe, 0x00, 0xc1, 0x61, 0x39, 0x3c, 0x44, 0xcc, 0xfd, 0x4b, 0xfe, 0x00,
0xff, 0xfb, 0x61, 0x3e, 0xfe, 0x41, 0xcb, 0x39, 0x3c, 0x44, 0xcc, 0xfd, 0x4b, 0xfe, 0x00, 0x37, 0x39, 0x5a,
0xae, 0xfd, 0x1f, 0x4b, 0x8e, 0x81, 0xfe, 0x3f, 0x39, 0x5a, 0x1c, 0x81, 0xc0, 0xff, 0x00, 0x00, 0x00, 0x30,
0xb1, 0xff, 0x00, 0x00, 0x05
]
end

context "with no phrases" do
let(:phrases) { [] }

it do
should eq [
0x0b, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x24, 0x00, 0x2e, 0x00, 0x38, 0x00, 0x47, 0x00, 0x56, 0x00, 0x60, 0x00,
0x6a, 0x00, 0x6f, 0x00, 0x79, 0x00, 0x83, 0x00, 0xf0, 0x53, 0xfd, 0x4d, 0x03, 0xb0, 0x8d, 0xfe, 0x00, 0x00,
0xf0, 0x53, 0xfd, 0x4d, 0x03, 0xb0, 0x7b, 0xfe, 0x00, 0x00, 0xf0, 0xfb, 0x63, 0x39, 0x3c, 0xc0, 0xfe, 0x00,
0x00, 0x00, 0xf0, 0xfb, 0x61, 0x39, 0x3c, 0x70, 0x94, 0xfd, 0x4b, 0x03, 0x7c, 0x44, 0x27, 0xfe, 0x00, 0xd3,
0xfb, 0xae, 0x30, 0x29, 0x33, 0x3c, 0xfb, 0x30, 0x20, 0x07, 0x3c, 0x39, 0x24, 0xfe, 0xf1, 0xfb, 0x53, 0x03,
0xae, 0x9c, 0xe6, 0x8e, 0xfe, 0x00, 0xc1, 0x61, 0x39, 0x3c, 0x44, 0xcc, 0xfd, 0x4b, 0xfe, 0x00, 0xff, 0xfb,
0x61, 0x3e, 0xfe, 0x41, 0xcb, 0x39, 0x3c, 0x44, 0xcc, 0xfd, 0x4b, 0xfe, 0x00, 0x37, 0x39, 0x5a, 0xae, 0xfd,
0x1f, 0x4b, 0x8e, 0x81, 0xfe, 0x3f, 0x39, 0x5a, 0x1c, 0x81, 0xc0, 0xff, 0x00, 0x00, 0x00, 0x05
]
end
end

context "with two phrases" do
let(:phrase_instance_2) { described_class.new(phrase: [0xb1, 0xb2, 0xb3]) }

let(:phrases) { [phrase_instance, phrase_instance_2] }

it do
should eq [
0x0d, 0x00, 0x1a, 0x00, 0x1e, 0x00, 0x28, 0x00, 0x32, 0x00, 0x3c, 0x00, 0x4b, 0x00, 0x5a, 0x00, 0x64, 0x00,
0x6e, 0x00, 0x73, 0x00, 0x7d, 0x00, 0x87, 0x00, 0x91, 0x00, 0x96, 0x00, 0xf0, 0x53, 0xfd, 0x4d, 0x03, 0xb0,
0x8d, 0xfe, 0x00, 0x00, 0xf0, 0x53, 0xfd, 0x4d, 0x03, 0xb0, 0x7b, 0xfe, 0x00, 0x00, 0xf0, 0xfb, 0x63, 0x39,
0x3c, 0xc0, 0xfe, 0x00, 0x00, 0x00, 0xf0, 0xfb, 0x61, 0x39, 0x3c, 0x70, 0x94, 0xfd, 0x4b, 0x03, 0x7c, 0x44,
0x27, 0xfe, 0x00, 0xd3, 0xfb, 0xae, 0x30, 0x29, 0x33, 0x3c, 0xfb, 0x30, 0x20, 0x07, 0x3c, 0x39, 0x24, 0xfe,
0xf1, 0xfb, 0x53, 0x03, 0xae, 0x9c, 0xe6, 0x8e, 0xfe, 0x00, 0xc1, 0x61, 0x39, 0x3c, 0x44, 0xcc, 0xfd, 0x4b,
0xfe, 0x00, 0xff, 0xfb, 0x61, 0x3e, 0xfe, 0x41, 0xcb, 0x39, 0x3c, 0x44, 0xcc, 0xfd, 0x4b, 0xfe, 0x00, 0x37,
0x39, 0x5a, 0xae, 0xfd, 0x1f, 0x4b, 0x8e, 0x81, 0xfe, 0x3f, 0x39, 0x5a, 0x1c, 0x81, 0xc0, 0xff, 0x00, 0x00,
0x00, 0x30, 0xb1, 0xfe, 0x00, 0x00, 0x03, 0xb1, 0xb2, 0xb3, 0xff, 0x05
]
end
end
end

describe "#phrase" do
subject(:phrase_value) { phrase_instance.phrase }

it { should eq([0xb1]) }

context "when phrase is [0xb1, 0xb2, 0xb3]" do
let(:phrase) { [0xb1, 0xb2, 0xb3] }

it { should eq([0xb1, 0xb2, 0xb3]) }
end
end
end
45 changes: 44 additions & 1 deletion spec/lib/timex_datalink_client/protocol_7/eeprom_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
describe TimexDatalinkClient::Protocol7::Eeprom do
let(:activities) { nil }
let(:phone_numbers) { nil }
let(:phrases) { nil }

let(:eeprom) do
described_class.new(
activities: activities,
phone_numbers: phone_numbers
phone_numbers: phone_numbers,
phrases: phrases
)
end

Expand Down Expand Up @@ -73,5 +75,46 @@
[0x92, 0x05]
]
end

context "with phrases present" do
let(:phrases) do
[
TimexDatalinkClient::Protocol7::Eeprom::Phrase.new(
phrase: [0xb1, 0xb2, 0xb3]
),
TimexDatalinkClient::Protocol7::Eeprom::Phrase.new(
phrase: [0xb1, 0xb2, 0xb3]
)
]
end

it_behaves_like "CRC-wrapped packets", [
[
0x90, 0x05, 0x05, 0x44, 0x53, 0x49, 0x20, 0x54, 0x6f, 0x79, 0x73, 0x20, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e,
0x74, 0x73, 0x2e, 0x2e, 0x2e, 0x65, 0x42, 0x72, 0x61, 0x69, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00
],
[
0x91, 0x05, 0x01, 0x0d, 0x00, 0x1a, 0x00, 0x1e, 0x00, 0x28, 0x00, 0x32, 0x00, 0x3c, 0x00, 0x4b, 0x00, 0x5a,
0x00, 0x64, 0x00, 0x6e, 0x00, 0x73, 0x00, 0x7d, 0x00, 0x87, 0x00, 0x91, 0x00, 0x96, 0x00, 0xf0, 0x53
],
[
0x91, 0x05, 0x02, 0xfd, 0x4d, 0x03, 0xb0, 0x8d, 0xfe, 0x00, 0x00, 0xf0, 0x53, 0xfd, 0x4d, 0x03, 0xb0, 0x7b,
0xfe, 0x00, 0x00, 0xf0, 0xfb, 0x63, 0x39, 0x3c, 0xc0, 0xfe, 0x00, 0x00, 0x00, 0xf0, 0xfb, 0x61, 0x39
],
[
0x91, 0x05, 0x03, 0x3c, 0x70, 0x94, 0xfd, 0x4b, 0x03, 0x7c, 0x44, 0x27, 0xfe, 0x00, 0xd3, 0xfb, 0xae, 0x30,
0x29, 0x33, 0x3c, 0xfb, 0x30, 0x20, 0x07, 0x3c, 0x39, 0x24, 0xfe, 0xf1, 0xfb, 0x53, 0x03, 0xae, 0x9c
],
[
0x91, 0x05, 0x04, 0xe6, 0x8e, 0xfe, 0x00, 0xc1, 0x61, 0x39, 0x3c, 0x44, 0xcc, 0xfd, 0x4b, 0xfe, 0x00, 0xff,
0xfb, 0x61, 0x3e, 0xfe, 0x41, 0xcb, 0x39, 0x3c, 0x44, 0xcc, 0xfd, 0x4b, 0xfe, 0x00, 0x37, 0x39, 0x5a
],
[
0x91, 0x05, 0x05, 0xae, 0xfd, 0x1f, 0x4b, 0x8e, 0x81, 0xfe, 0x3f, 0x39, 0x5a, 0x1c, 0x81, 0xc0, 0xff, 0x00,
0x00, 0x00, 0x03, 0xb1, 0xb2, 0xb3, 0xfe, 0x03, 0xb1, 0xb2, 0xb3, 0xff, 0x05
],
[0x92, 0x05]
]
end
end
end
1 change: 1 addition & 0 deletions timex_datalink_client.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ Gem::Specification.new do |s|
"lib/timex_datalink_client/protocol_7/eeprom.rb",
"lib/timex_datalink_client/protocol_7/eeprom/activity.rb",
"lib/timex_datalink_client/protocol_7/eeprom/phone_number.rb",
"lib/timex_datalink_client/protocol_7/eeprom/phrase.rb",
"lib/timex_datalink_client/protocol_7/end.rb",
"lib/timex_datalink_client/protocol_7/start.rb",
"lib/timex_datalink_client/protocol_7/sync.rb",
Expand Down

0 comments on commit 657f6d8

Please sign in to comment.