Skip to content

Instantly share code, notes, and snippets.

@crsayen
Last active May 7, 2020 12:53
Show Gist options
  • Save crsayen/58ad65038e463c13fb9caa566fea2ce2 to your computer and use it in GitHub Desktop.
Save crsayen/58ad65038e463c13fb9caa566fea2ce2 to your computer and use it in GitHub Desktop.
using HHKBKeymapTool.Events;
using HHKBKeymapTool.Models.Definitions;
using Prism.Events;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace HHKBKeymapTool.Models
{
public class KeyboardDriver
{
private AppConfigData appConfigData;
private IUSBDriver driver;
private ToolUtility toolUtility;
private KeyboardLibrary keyboardLibrary;
private ILogger logger;
private LiteralResourcesLoader literalResources;
private IEventAggregator eventAggregator;
private const int GET_KEYBOARD_INFORMATION_HEADER_LENGTH = 6;
private const int GET_KEYBOARD_INFORMATION_TYPENUMBER_LENGTH = 20;
private const int GET_KEYBOARD_INFORMATION_REVISION_LENGTH = 4;
private const int GET_KEYBOARD_INFORMATION_SERIAL_LENGTH = 16;
private const int GET_KEYBOARD_INFORMATION_APP_FIRM_VERSION_LENGTH = 8;
private const int GET_KEYBOARD_INFORMATION_BOOT_FIRM_VERSION_LENGTH = 8;
private const int GET_KEYBOARD_INFORMATION_RUNNING_FIRMWARE_STATE_LENGTH = 1;
private const int SET_KEYMAP_SEND_LENGTH_FIRST = 57;
private const int SET_KEYMAP_SEND_LENGTH_SECOND = 59;
private const int SET_KEYMAP_SEND_LENGTH_THIRD = 12;
private const int GET_KEYMAP_SEND_LENGTH_FIRST = 58;
private const int GET_KEYMAP_SEND_LENGTH_SECOND = 58;
private const int GET_KEYMAP_SEND_LENGTH_THIRD = 12;
private const int GET_FIRMWARE_LENGTH_HEADER = 5;
private const int GET_FIRMWARE_LENGTH_DATALENGTH = 1;
private const int GET_FIRMWARE_LENGTH_DATANUMBER = 2;
private const int GET_FIRMWARE_MAX_DATALENGTH = 58;
private const int FIRMUP_SEND_BUFFER_MAX_LENGTH = 57;
public KeyboardDriver(
AppConfigData appConfigData,
IUSBDriver driver,
ToolUtility toolUtility,
KeyboardLibrary keyboardLibrary,
ILogger logger,
LiteralResourcesLoader literalResources,
IEventAggregator eventAggregator)
{
this.appConfigData = appConfigData;
this.driver = driver;
this.toolUtility = toolUtility;
this.keyboardLibrary = keyboardLibrary;
this.logger = logger;
this.literalResources = literalResources;
this.eventAggregator = eventAggregator;
}
private bool Connect()
{
return this.driver.Connect(this.literalResources.VendorId, this.literalResources.ProductIds, this.literalResources.HardwareIdPattern);
}
private void Disconnect()
{
this.driver.Disconnect();
}
private bool Write(byte[] input, string id)
{
try
{
if (this.driver.Send(input, this.literalResources.TimeoutTimeForUSBCommunication) < 0)
{
this.logger.Error(string.Format("Keyboard Communication Error {0} (send)", (object) id), nameof (Write), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 108);
this.logger.Error(string.Format("sendData = {0}", (object) BitConverter.ToString(input)), nameof (Write), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 109);
this.driver.Disconnect();
return false;
}
}
catch (TimeoutException ex)
{
this.logger.Error(string.Format("Keyboard Communication Error {0} (timeout)", (object) id), nameof (Write), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 116);
return false;
}
return true;
}
private byte[] WriteReadCheck(byte[] input, byte[] prefix, string id)
{
return !this.Write(input, id) ? (byte[]) null : this.ReadCheck(prefix, id);
}
private bool ValidateReceivedData(byte[] receivedData, byte[] prefix)
{
return receivedData.Length == ConstDefinition.BufferSizeUSB && ((IEnumerable<byte>) receivedData).Take<byte>(prefix.Length - 1).SequenceEqual<byte>(((IEnumerable<byte>) prefix).Take<byte>(prefix.Length - 1)) && (int) receivedData[prefix.Length - 1] >= (int) prefix[prefix.Length - 1];
}
private byte[] ReadCheck(byte[] prefix, string id)
{
byte[] receivedData;
try
{
receivedData = this.driver.Receive(this.literalResources.TimeoutTimeForUSBCommunication);
}
catch (TimeoutException ex)
{
this.logger.Error(string.Format("Keyboard Communication Error {0} (timeout)", (object) id), nameof (ReadCheck), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 188);
return (byte[]) null;
}
if (receivedData == null)
{
this.logger.Error(string.Format("Keyboard Communication Error {0} (receive)", (object) id), nameof (ReadCheck), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 194);
return (byte[]) null;
}
if (this.ValidateReceivedData(receivedData, prefix))
return receivedData;
this.logger.Error(string.Format("Keyboard Communication Error {0} (format error)", (object) id), nameof (ReadCheck), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 202);
this.logger.Error(string.Format("receiveData = {0}", (object) BitConverter.ToString(receivedData)), nameof (ReadCheck), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 203);
return (byte[]) null;
}
private WriteToHHKBResult CheckSameKeyboard(KeyboardData keyboardData)
{
this.logger.Info("KeyboardDriver.CheckSameKeyboard Start", nameof (CheckSameKeyboard), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 217);
GetKeyboardInformationDataclass keyboardInformation = this.GetKeyboardInformation();
if (keyboardInformation == null)
{
this.logger.Error("Can't get keyboard information.", nameof (CheckSameKeyboard), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 222);
return WriteToHHKBResult.CommunicationError;
}
bool[] dipswInformation = this.GetDipswInformation();
if (dipswInformation == null)
{
this.logger.Error("Can't get the states of dipswtichs.", nameof (CheckSameKeyboard), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 228);
return WriteToHHKBResult.CommunicationError;
}
KeyboardMode? keyboardMode = this.GetKeyboardMode();
if (!keyboardMode.HasValue)
{
this.logger.Error("Can't get the keyboard mode.", nameof (CheckSameKeyboard), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 234);
return WriteToHHKBResult.CommunicationError;
}
if (keyboardData.Serial != keyboardInformation.Serial)
{
this.logger.Error(string.Format("Different Keyboard (Initial:{0}, Current:{1})", (object) keyboardData.Serial, (object) keyboardInformation.Serial), nameof (CheckSameKeyboard), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 241);
return WriteToHHKBResult.KeyboardMismatchError;
}
if (!((IEnumerable<bool>) keyboardData.DipSwitch).SequenceEqual<bool>((IEnumerable<bool>) dipswInformation))
{
this.logger.Error(string.Format("Different Dipswitch (Initial:[{0}], Current:[{1}])", (object) string.Join(", ", ((IEnumerable<bool>) keyboardData.DipSwitch).Select<bool, string>((Func<bool, string>) (b => b.ToString())).ToArray<string>()), (object) string.Join(", ", ((IEnumerable<bool>) dipswInformation).Select<bool, string>((Func<bool, string>) (b => b.ToString())).ToArray<string>())), nameof (CheckSameKeyboard), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 250);
return WriteToHHKBResult.KeyboardMismatchError;
}
int mode = (int) keyboardData.Mode;
KeyboardMode? nullable = keyboardMode;
int valueOrDefault = (int) nullable.GetValueOrDefault();
if (mode == valueOrDefault & nullable.HasValue)
return WriteToHHKBResult.Success;
this.logger.Error(string.Format("Different Mode (Initial:[{0}], Current:[{1}])", (object) keyboardData.Mode.ToString(), (object) keyboardMode.ToString()), nameof (CheckSameKeyboard), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 257);
return WriteToHHKBResult.ModeMismatchError;
}
private bool NotifyApplicationState(ApplicationState state)
{
byte[] input = new byte[ConstDefinition.BufferSizeUSB];
input[0] = input[1] = (byte) 170;
input[2] = (byte) 1;
input[3] = (byte) 0;
input[4] = (byte) 1;
input[5] = (byte) state;
return this.WriteReadCheck(input, new byte[6]
{
(byte) 85,
(byte) 85,
(byte) 1,
(byte) 0,
(byte) 0,
(byte) 0
}, "0x01") != null;
}
private GetKeyboardInformationDataclass GetKeyboardInformation()
{
byte[] input = new byte[ConstDefinition.BufferSizeUSB];
input[0] = input[1] = (byte) 170;
input[2] = (byte) 2;
byte[] prefix = new byte[6]
{
(byte) 85,
(byte) 85,
(byte) 2,
(byte) 0,
(byte) 0,
(byte) 48
};
byte[] numArray = this.WriteReadCheck(input, prefix, "0x02");
if (numArray == null)
return (GetKeyboardInformationDataclass) null;
int count1 = 6;
byte[] array1 = ((IEnumerable<byte>) numArray).Skip<byte>(count1).Take<byte>(20).ToArray<byte>();
int count2 = count1 + 20;
byte[] array2 = ((IEnumerable<byte>) numArray).Skip<byte>(count2).Take<byte>(4).ToArray<byte>();
int count3 = count2 + 4;
byte[] array3 = ((IEnumerable<byte>) numArray).Skip<byte>(count3).Take<byte>(16).ToArray<byte>();
int count4 = count3 + 16;
byte[] array4 = ((IEnumerable<byte>) numArray).Skip<byte>(count4).Take<byte>(8).ToArray<byte>();
int count5 = count4 + 8;
byte[] array5 = ((IEnumerable<byte>) numArray).Skip<byte>(count5).Take<byte>(8).ToArray<byte>();
int count6 = count5 + 8;
byte[] array6 = ((IEnumerable<byte>) numArray).Skip<byte>(count6).Take<byte>(1).ToArray<byte>();
return new GetKeyboardInformationDataclass()
{
TypeNumber = Encoding.ASCII.GetString(array1).TrimEnd(new char[1]),
Revision = Encoding.ASCII.GetString(array2).TrimEnd(new char[1]),
Serial = Encoding.ASCII.GetString(array3).TrimEnd(new char[1]),
AppFirmVersion = ToolUtility.ParseVersion(array4),
BootFirmVersion = ToolUtility.ParseVersion(array5),
RunningFirmware = (RunningFirmwareState) array6[0]
};
}
private bool ResetToFactoryDefault()
{
byte[] input = new byte[ConstDefinition.BufferSizeUSB];
input[0] = input[1] = (byte) 170;
input[2] = (byte) 3;
return this.WriteReadCheck(input, new byte[6]
{
(byte) 85,
(byte) 85,
(byte) 3,
(byte) 0,
(byte) 0,
(byte) 0
}, "0x03") != null;
}
private bool ConfirmKeymap()
{
byte[] input = new byte[ConstDefinition.BufferSizeUSB];
input[0] = input[1] = (byte) 170;
input[2] = (byte) 4;
return this.WriteReadCheck(input, new byte[6]
{
(byte) 85,
(byte) 85,
(byte) 4,
(byte) 0,
(byte) 0,
(byte) 0
}, "0x04") != null;
}
private bool[] GetDipswInformation()
{
byte[] input = new byte[ConstDefinition.BufferSizeUSB];
input[0] = input[1] = (byte) 170;
input[2] = (byte) 5;
byte[] numArray = this.WriteReadCheck(input, new byte[6]
{
(byte) 85,
(byte) 85,
(byte) 5,
(byte) 0,
(byte) 0,
(byte) 12
}, "0x05");
if (numArray == null)
return (bool[]) null;
bool[] flagArray = new bool[6];
for (int index = 0; index < 6; ++index)
flagArray[index] = numArray[6 + index] != (byte) 0;
return flagArray;
}
private KeyboardMode? GetKeyboardMode()
{
byte[] input = new byte[ConstDefinition.BufferSizeUSB];
input[0] = input[1] = (byte) 170;
input[2] = (byte) 6;
byte[] numArray = this.WriteReadCheck(input, new byte[6]
{
(byte) 85,
(byte) 85,
(byte) 6,
(byte) 0,
(byte) 0,
(byte) 1
}, "0x06");
if (numArray == null)
return new KeyboardMode?();
switch (numArray[6])
{
case 0:
return new KeyboardMode?(KeyboardMode.HHK_Mode);
case 1:
return new KeyboardMode?(KeyboardMode.Mac_Mode);
case 2:
return new KeyboardMode?(KeyboardMode.Lite_Mode);
default:
this.logger.Error(string.Format("Keyboard Communication Error 0x06 (invalid mode number)"), nameof (GetKeyboardMode), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 414);
this.logger.Error(string.Format("receiveData = {0}", (object) BitConverter.ToString(numArray)), nameof (GetKeyboardMode), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 415);
return new KeyboardMode?();
}
}
private bool ResetDIPSW(KeyboardMode keyboardMode)
{
byte[] input = new byte[ConstDefinition.BufferSizeUSB];
input[0] = input[1] = (byte) 170;
input[2] = (byte) 7;
input[3] = (byte) 0;
input[4] = (byte) 1;
input[5] = (byte) keyboardMode;
return this.WriteReadCheck(input, new byte[6]
{
(byte) 85,
(byte) 85,
(byte) 7,
(byte) 0,
(byte) 0,
(byte) 0
}, "0x07") != null;
}
private bool SetKeymap(KeyboardMode mode, KeymapFnKey fnKey, int[] keymapData)
{
byte[] input1 = new byte[ConstDefinition.BufferSizeUSB];
input1[0] = input1[1] = (byte) 170;
input1[2] = (byte) 134;
input1[3] = (byte) 65;
input1[4] = (byte) 59;
input1[5] = (byte) mode;
input1[6] = (byte) fnKey;
for (int index = 0; index < 57; ++index)
input1[7 + index] = (byte) keymapData[index];
if (this.WriteReadCheck(input1, new byte[6]
{
(byte) 85,
(byte) 85,
(byte) 134,
(byte) 0,
(byte) 0,
(byte) 0
}, "0x86-1") == null)
return false;
byte[] input2 = new byte[ConstDefinition.BufferSizeUSB];
input2[0] = input2[1] = (byte) 170;
input2[2] = (byte) 134;
input2[3] = (byte) 130;
input2[4] = (byte) 59;
for (int index = 0; index < 59; ++index)
input2[5 + index] = (byte) keymapData[57 + index];
if (this.WriteReadCheck(input2, new byte[6]
{
(byte) 85,
(byte) 85,
(byte) 134,
(byte) 0,
(byte) 0,
(byte) 0
}, "0x86-2") == null)
return false;
byte[] input3 = new byte[ConstDefinition.BufferSizeUSB];
input3[0] = input3[1] = (byte) 170;
input3[2] = (byte) 134;
input3[3] = (byte) 195;
input3[4] = (byte) 12;
for (int index = 0; index < 12; ++index)
input3[5 + index] = (byte) keymapData[116 + index];
return this.WriteReadCheck(input3, new byte[6]
{
(byte) 85,
(byte) 85,
(byte) 134,
(byte) 0,
(byte) 0,
(byte) 0
}, "0x86-3") != null;
}
private int[] GetKeymap(KeyboardMode mode, KeymapFnKey fnKey)
{
byte[] input = new byte[ConstDefinition.BufferSizeUSB];
input[0] = input[1] = (byte) 170;
input[2] = (byte) 135;
input[4] = (byte) 2;
input[5] = (byte) mode;
input[6] = (byte) fnKey;
byte[] numArray1 = this.WriteReadCheck(input, new byte[6]
{
(byte) 85,
(byte) 85,
(byte) 135,
(byte) 0,
(byte) 65,
(byte) 58
}, "0x87-1");
if (numArray1 == null)
return (int[]) null;
int[] numArray2 = new int[128];
for (int index = 0; index < 58; ++index)
numArray2[index] = (int) numArray1[6 + index];
byte[] numArray3 = this.ReadCheck(new byte[6]
{
(byte) 85,
(byte) 85,
(byte) 135,
(byte) 0,
(byte) 130,
(byte) 58
}, "0x87-2");
if (numArray3 == null)
return (int[]) null;
for (int index = 0; index < 58; ++index)
numArray2[58 + index] = (int) numArray3[6 + index];
byte[] numArray4 = this.ReadCheck(new byte[6]
{
(byte) 85,
(byte) 85,
(byte) 135,
(byte) 0,
(byte) 195,
(byte) 12
}, "0x87-3");
if (numArray4 == null)
return (int[]) null;
for (int index = 0; index < 12; ++index)
numArray2[116 + index] = (int) numArray4[6 + index];
return numArray2;
}
private async Task<List<byte[]>> GetFirmwareCommand()
{
KeyboardDriver keyboardDriver1 = this;
List<byte[]> response = new List<byte[]>();
byte[] input = new byte[ConstDefinition.BufferSizeUSB];
input[0] = input[1] = (byte) 170;
input[2] = (byte) 208;
input[3] = input[4] = (byte) 0;
if (!keyboardDriver1.Write(input, "0xD0-1"))
{
keyboardDriver1.logger.Error("Can't start dumping the firmware.", nameof (GetFirmwareCommand), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 586);
return (List<byte[]>) null;
}
while (true)
{
KeyboardDriver keyboardDriver = keyboardDriver1;
byte[] output = (byte[]) null;
await Task.Run((Action) (() => output = keyboardDriver.ReadCheck(new byte[6]
{
(byte) 85,
(byte) 85,
(byte) 208,
(byte) 0,
(byte) 0,
(byte) 0
}, "0xD0-2")));
if (output != null)
{
int count1 = 5;
int num1 = (int) ((IEnumerable<byte>) output).Skip<byte>(count1).First<byte>();
int count2 = count1 + 1;
uint num2 = (uint) ToolUtility.ConvertBytesToUshort(((IEnumerable<byte>) output).Skip<byte>(count2).Take<byte>(2).ToArray<byte>());
response.Add(((IEnumerable<byte>) output).Skip<byte>(count2 + 2).Take<byte>(num1 - 2).ToArray<byte>());
if (num1 >= 58)
;
else
goto label_7;
}
else
break;
}
keyboardDriver1.logger.Error("Can't read the dump data.", nameof (GetFirmwareCommand), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 602);
return (List<byte[]>) null;
label_7:
return response;
}
private bool FirmupModeChange()
{
byte[] input = new byte[ConstDefinition.BufferSizeUSB];
input[0] = input[1] = (byte) 170;
input[2] = (byte) 224;
return this.WriteReadCheck(input, new byte[6]
{
(byte) 85,
(byte) 85,
(byte) 224,
(byte) 0,
(byte) 0,
(byte) 0
}, "0xE0") != null;
}
private bool FirmupStart(uint firmSize, byte[] crc)
{
byte[] input = new byte[ConstDefinition.BufferSizeUSB];
input[0] = input[1] = (byte) 170;
input[2] = (byte) 225;
input[3] = (byte) 0;
input[4] = (byte) 8;
byte[] bytes = this.toolUtility.ConvertUintToBytes(firmSize);
for (int index = 0; index < 4; ++index)
input[5 + index] = bytes[index];
input[9] = crc[0];
input[10] = crc[1];
return this.WriteReadCheck(input, new byte[6]
{
(byte) 85,
(byte) 85,
(byte) 225,
(byte) 0,
(byte) 0,
(byte) 0
}, "0xE1") != null;
}
private async Task<bool> FirmupSend(uint dataNumber, byte[] data)
{
KeyboardDriver keyboardDriver1 = this;
keyboardDriver1.logger.Info("[FirmupSend] 1", nameof (FirmupSend), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 675);
dataNumber -= 2U;
data = ((IEnumerable<byte>) data).Skip<byte>(2).ToArray<byte>();
keyboardDriver1.logger.Info("[FirmupSend] 3", nameof (FirmupSend), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 681);
uint numberOfPacket = (uint) ((int) dataNumber + 57 - 1) / 57U;
uint completedByteLength = 0;
for (ushort packetNumber = 0; (uint) packetNumber < numberOfPacket; ++packetNumber)
{
KeyboardDriver keyboardDriver = keyboardDriver1;
keyboardDriver1.logger.Info(string.Format("[FirmupSend] 4 ({0}", (object) packetNumber), nameof (FirmupSend), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 690);
uint num1 = (uint) packetNumber * 57U;
uint num2 = (uint) (((int) packetNumber + 1) * 57);
if (dataNumber <= num2)
num2 = dataNumber;
uint length = num2 - num1;
byte[] input = new byte[ConstDefinition.BufferSizeUSB];
input[0] = input[1] = (byte) 170;
input[2] = (byte) 226;
input[4] = (byte) (length + 2U);
byte[] packetNumberByte = keyboardDriver1.toolUtility.ConvertUshortToBytes(packetNumber);
input[5] = packetNumberByte[0];
input[6] = packetNumberByte[1];
for (int index = 0; (long) index < (long) length; ++index)
input[7 + index] = data[(long) num1 + (long) index];
byte[] output = new byte[0];
keyboardDriver1.logger.Info(string.Format("[FirmupSend] 5 ({0}", (object) packetNumber), nameof (FirmupSend), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 717);
await Task.Run((Action) (() => output = keyboardDriver.WriteReadCheck(input, new byte[6]
{
(byte) 85,
(byte) 85,
(byte) 226,
(byte) 0,
(byte) 0,
(byte) 2
}, "0xE2")));
keyboardDriver1.logger.Info(string.Format("[FirmupSend] 6 ({0}", (object) packetNumber), nameof (FirmupSend), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 725);
if (output == null)
return false;
if ((int) output[6] != (int) packetNumberByte[0] || (int) output[7] != (int) packetNumberByte[1])
{
keyboardDriver1.logger.Error(string.Format("Keyboard Communication Error 0xE2 (format error)"), nameof (FirmupSend), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 736);
keyboardDriver1.logger.Error(string.Format("receiveData = {0}", (object) BitConverter.ToString(output)), nameof (FirmupSend), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 737);
return false;
}
completedByteLength += length;
int completedRate = 10 + (int) (completedByteLength * 80U / dataNumber);
keyboardDriver1.eventAggregator.GetEvent<NortificateFirmupProgressEvent>().Publish(new NortificateFirmupProgressEventEntity(completedRate));
packetNumberByte = (byte[]) null;
}
keyboardDriver1.logger.Info("[FirmupSend] 7", nameof (FirmupSend), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 748);
return true;
}
private bool FirmupEnd()
{
byte[] input = new byte[ConstDefinition.BufferSizeUSB];
input[0] = input[1] = (byte) 170;
input[2] = (byte) 227;
return this.WriteReadCheck(input, new byte[6]
{
(byte) 85,
(byte) 85,
(byte) 227,
(byte) 0,
(byte) 0,
(byte) 0
}, "0xE3") != null;
}
private bool UpdateBootModeChange()
{
byte[] input = new byte[ConstDefinition.BufferSizeUSB];
input[0] = input[1] = (byte) 170;
input[2] = (byte) 228;
return this.WriteReadCheck(input, new byte[6]
{
(byte) 85,
(byte) 85,
(byte) 228,
(byte) 0,
(byte) 0,
(byte) 0
}, "0xE4") != null;
}
private bool UpdateBootStart(uint firmSize, byte[] crc)
{
byte[] input = new byte[ConstDefinition.BufferSizeUSB];
input[0] = input[1] = (byte) 170;
input[2] = (byte) 229;
input[4] = (byte) 8;
byte[] bytes = this.toolUtility.ConvertUintToBytes(firmSize);
for (int index = 0; index < 4; ++index)
input[5 + index] = bytes[index];
input[9] = crc[0];
input[10] = crc[1];
return this.WriteReadCheck(input, new byte[6]
{
(byte) 85,
(byte) 85,
(byte) 229,
(byte) 0,
(byte) 0,
(byte) 0
}, "0xE5") != null;
}
private async Task<bool> UpdateBootSend(uint dataNumber, byte[] data)
{
KeyboardDriver keyboardDriver1 = this;
dataNumber -= 2U;
data = ((IEnumerable<byte>) data).Skip<byte>(2).ToArray<byte>();
uint numberOfPacket = (uint) ((int) dataNumber + 57 - 1) / 57U;
uint completedByteLength = 0;
for (ushort packetNumber = 0; (uint) packetNumber < numberOfPacket; ++packetNumber)
{
KeyboardDriver keyboardDriver = keyboardDriver1;
uint num1 = (uint) packetNumber * 57U;
uint num2 = (uint) (((int) packetNumber + 1) * 57);
if (dataNumber <= num2)
num2 = dataNumber;
uint length = num2 - num1;
byte[] input = new byte[ConstDefinition.BufferSizeUSB];
input[0] = input[1] = (byte) 170;
input[2] = (byte) 230;
input[4] = (byte) (length + 2U);
byte[] packetNumberByte = keyboardDriver1.toolUtility.ConvertUshortToBytes(packetNumber);
input[5] = packetNumberByte[0];
input[6] = packetNumberByte[1];
for (int index = 0; (long) index < (long) length; ++index)
input[7 + index] = data[(long) num1 + (long) index];
byte[] output = new byte[1];
await Task.Run((Action) (() => output = keyboardDriver.WriteReadCheck(input, new byte[6]
{
(byte) 85,
(byte) 85,
(byte) 230,
(byte) 0,
(byte) 0,
(byte) 2
}, "0xE6")));
if (output == null)
return false;
if ((int) output[6] != (int) packetNumberByte[0] || (int) output[7] != (int) packetNumberByte[1])
{
keyboardDriver1.logger.Error(string.Format("Keyboard Communication Error 0xE6 (format error)"), nameof (UpdateBootSend), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 861);
keyboardDriver1.logger.Error(string.Format("receiveData = {0}", (object) BitConverter.ToString(output)), nameof (UpdateBootSend), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 862);
return false;
}
completedByteLength += length;
int completedRate = 10 + (int) (completedByteLength * 80U / dataNumber);
keyboardDriver1.eventAggregator.GetEvent<NortificateFirmupProgressEvent>().Publish(new NortificateFirmupProgressEventEntity(completedRate));
packetNumberByte = (byte[]) null;
}
return true;
}
private bool UpdateBootEnd()
{
byte[] input = new byte[ConstDefinition.BufferSizeUSB];
input[0] = input[1] = (byte) 170;
input[2] = (byte) 231;
return this.WriteReadCheck(input, new byte[6]
{
(byte) 85,
(byte) 85,
(byte) 231,
(byte) 0,
(byte) 0,
(byte) 0
}, "0xE7") != null;
}
public KeyboardData GetAllKeyboardInformation()
{
KeyboardData keyboardData = new KeyboardData();
if (this.appConfigData.isUsingManualKeyboard)
{
keyboardData.TypeNumber = this.appConfigData.typeNumber;
keyboardData.Revision = "";
keyboardData.Serial = "";
keyboardData.FirmVersion = this.appConfigData.firmVersion;
keyboardData.DipSwitch = this.appConfigData.dipSwitch;
keyboardData.Keymap = this.appConfigData.keymapData;
keyboardData.Mode = this.keyboardLibrary.GetKeyboardMode(keyboardData.TypeNumber, keyboardData.DipSwitch);
keyboardData.RunningFirmware = this.appConfigData.runningFirmware;
this.logger.Info("Keyboard Information : " + keyboardData.ToString(), nameof (GetAllKeyboardInformation), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 907);
return keyboardData;
}
if (!this.Connect())
{
this.logger.Info("Can't connect any keyboards.", nameof (GetAllKeyboardInformation), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 915);
return (KeyboardData) null;
}
try
{
if (!this.NotifyApplicationState(ApplicationState.Open))
{
this.logger.Info("Can't notify Open Status.", nameof (GetAllKeyboardInformation), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 923);
return (KeyboardData) null;
}
GetKeyboardInformationDataclass keyboardInformation = this.GetKeyboardInformation();
if (keyboardInformation == null)
{
this.logger.Info("Can't get keyboard information", nameof (GetAllKeyboardInformation), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 931);
return (KeyboardData) null;
}
keyboardData.TypeNumber = keyboardInformation.TypeNumber;
keyboardData.Revision = keyboardInformation.Revision;
keyboardData.Serial = keyboardInformation.Serial;
keyboardData.FirmVersion = keyboardInformation.AppFirmVersion;
keyboardData.BootFirmVersion = keyboardInformation.BootFirmVersion;
keyboardData.RunningFirmware = keyboardInformation.RunningFirmware;
keyboardData.DipSwitch = this.GetDipswInformation();
if (keyboardData.DipSwitch == null)
{
this.logger.Info("Can't get DIP switch", nameof (GetAllKeyboardInformation), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 946);
return (KeyboardData) null;
}
keyboardData.Keymap = new KeymapData();
KeyboardMode keyboardMode1 = this.keyboardLibrary.GetKeyboardMode(keyboardData.TypeNumber, keyboardData.DipSwitch);
if (keyboardMode1 != KeyboardMode.Secret_Mode)
{
keyboardData.Keymap.Normal = this.GetKeymap(keyboardMode1, KeymapFnKey.Normal);
keyboardData.Keymap.WithFn = this.GetKeymap(keyboardMode1, KeymapFnKey.WithFn);
if (keyboardData.Keymap.Normal == null || keyboardData.Keymap.Normal == null)
{
this.logger.Info("Can't get the keymaps", nameof (GetAllKeyboardInformation), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 960);
return (KeyboardData) null;
}
}
keyboardData.ConnectedProductId = this.driver.ConnectedProductId;
KeyboardMode? keyboardMode2 = this.GetKeyboardMode();
if (!keyboardMode2.HasValue)
{
this.logger.Info("Can't get the mode.", nameof (GetAllKeyboardInformation), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 970);
return (KeyboardData) null;
}
keyboardData.Mode = keyboardMode2.Value;
this.logger.Info("Keyboard Information : " + keyboardData.ToString(), nameof (GetAllKeyboardInformation), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 975);
return keyboardData;
}
finally
{
this.NotifyApplicationState(ApplicationState.Close);
this.Disconnect();
}
}
public async Task<WriteToHHKBResult> Firmup(
FirmData firmData,
KeyboardData keyboardData)
{
if (this.appConfigData.isUsingManualKeyboard)
{
await Task.Delay(10000);
return WriteToHHKBResult.Success;
}
if (!this.Connect())
{
this.logger.Info("Can't connect any keyboards.", nameof (Firmup), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 1006);
return WriteToHHKBResult.CommunicationError;
}
try
{
this.logger.Info("Firmup Start", nameof (Firmup), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 1012);
if (!this.NotifyApplicationState(ApplicationState.Open))
{
this.logger.Info("Can't notify Open Status.", nameof (Firmup), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 1016);
return WriteToHHKBResult.CommunicationError;
}
WriteToHHKBResult writeToHhkbResult = this.CheckSameKeyboard(keyboardData);
if (writeToHhkbResult != WriteToHHKBResult.Success)
{
this.logger.Error("Connection Error or CheckSameKeyboard Error", nameof (Firmup), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 1024);
return writeToHhkbResult;
}
if (!this.FirmupModeChange())
{
this.logger.Info("Can't move to firmup state.", nameof (Firmup), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 1031);
return WriteToHHKBResult.CommunicationError;
}
}
finally
{
this.Disconnect();
}
int count;
for (count = 0; count < 10; ++count)
{
this.eventAggregator.GetEvent<NortificateFirmupProgressEvent>().Publish(new NortificateFirmupProgressEventEntity(count));
await Task.Delay(1000);
}
if (!this.Connect())
{
this.logger.Info("Can't connect", nameof (Firmup), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 1049);
return WriteToHHKBResult.CommunicationError;
}
try
{
if (!this.FirmupStart(firmData.FirmSize, firmData.CRC))
{
this.logger.Info("Can't start firmup.", nameof (Firmup), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 1057);
return WriteToHHKBResult.CommunicationError;
}
if (!await this.FirmupSend(firmData.FirmSize, firmData.FirmRawData))
{
this.logger.Info("Can't send firmdata.", nameof (Firmup), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 1063);
return WriteToHHKBResult.CommunicationError;
}
if (!this.FirmupEnd())
{
this.logger.Info("Can't end firmup.", nameof (Firmup), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 1069);
return WriteToHHKBResult.CommunicationError;
}
}
finally
{
this.Disconnect();
}
for (count = 0; count < 10; ++count)
{
this.eventAggregator.GetEvent<NortificateFirmupProgressEvent>().Publish(new NortificateFirmupProgressEventEntity(count + 90));
await Task.Delay(1000);
}
if (!this.Connect())
{
this.logger.Info("Can't reconnect the keyboard.", nameof (Firmup), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 1088);
return WriteToHHKBResult.CommunicationError;
}
this.logger.Info("OK", nameof (Firmup), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 1092);
this.Disconnect();
return WriteToHHKBResult.Success;
}
public WriteToHHKBResult WriteAllKeymaps(
KeymapData keymapData,
KeyboardData keyboardData)
{
if (this.appConfigData.isUsingManualKeyboard)
{
keyboardData.Keymap = new KeymapData(keymapData);
return WriteToHHKBResult.Success;
}
if (!this.Connect())
{
this.logger.Info("Can't connect any keyboards.", nameof (WriteAllKeymaps), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 1115);
return WriteToHHKBResult.CommunicationError;
}
try
{
this.logger.Info("WriteKeymap Start", nameof (WriteAllKeymaps), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 1121);
if (!this.NotifyApplicationState(ApplicationState.Open))
{
this.logger.Info("Can't notify Open Status.", nameof (WriteAllKeymaps), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 1125);
return WriteToHHKBResult.CommunicationError;
}
WriteToHHKBResult writeToHhkbResult = this.CheckSameKeyboard(keyboardData);
if (writeToHhkbResult != WriteToHHKBResult.Success)
{
this.logger.Error("Connection Error or CheckSameKeyboard Error", nameof (WriteAllKeymaps), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 1133);
return writeToHhkbResult;
}
KeyboardMode? nullable = new KeyboardMode?(this.keyboardLibrary.GetKeyboardMode(keyboardData.TypeNumber, keyboardData.DipSwitch));
if (!nullable.HasValue)
{
this.logger.Error("Can't get keyboard mode.", nameof (WriteAllKeymaps), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 1141);
return WriteToHHKBResult.CommunicationError;
}
if (!this.SetKeymap(nullable.Value, KeymapFnKey.Normal, keymapData.Normal))
{
this.logger.Error("Can't set keymap (normal)", nameof (WriteAllKeymaps), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 1147);
return WriteToHHKBResult.CommunicationError;
}
if (!this.SetKeymap(nullable.Value, KeymapFnKey.WithFn, keymapData.WithFn))
{
this.logger.Error("Can't set keymap (withfn)", nameof (WriteAllKeymaps), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 1153);
return WriteToHHKBResult.CommunicationError;
}
if (!this.ConfirmKeymap())
{
this.logger.Error("Can't confirm.", nameof (WriteAllKeymaps), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 1159);
return WriteToHHKBResult.CommunicationError;
}
if (!this.ResetDIPSW(nullable.Value))
{
this.logger.Error("Can't reset dipsw.", nameof (WriteAllKeymaps), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 1165);
return WriteToHHKBResult.CommunicationError;
}
this.logger.Error("OK", nameof (WriteAllKeymaps), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 1169);
keyboardData.Keymap = new KeymapData(keymapData);
return WriteToHHKBResult.Success;
}
finally
{
this.NotifyApplicationState(ApplicationState.Close);
this.Disconnect();
}
}
public async Task<WriteToHHKBResult> BootFirmup(
FirmData firmData,
KeyboardData keyboardData)
{
this.logger.Info("BootFirmup Start", nameof (BootFirmup), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 1190);
if (!this.Connect())
{
this.logger.Info("Can't connect any keyboards.", nameof (BootFirmup), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 1195);
return WriteToHHKBResult.CommunicationError;
}
this.logger.Info("[BootFirmup] 1", nameof (BootFirmup), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 1199);
try
{
if (!this.NotifyApplicationState(ApplicationState.Open))
{
this.logger.Info("Can't notify Open Status.", nameof (BootFirmup), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 1205);
return WriteToHHKBResult.CommunicationError;
}
this.logger.Info("[BootFirmup] 2", nameof (BootFirmup), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 1209);
if (!this.UpdateBootModeChange())
{
this.logger.Info("Can't move to bootfirmup state.", nameof (BootFirmup), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 1214);
return WriteToHHKBResult.CommunicationError;
}
this.logger.Info("[BootFirmup] 3", nameof (BootFirmup), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 1218);
}
finally
{
this.logger.Info("[BootFirmup] 4", nameof (BootFirmup), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 1222);
this.Disconnect();
this.logger.Info("[BootFirmup] 5", nameof (BootFirmup), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 1226);
}
int count;
for (count = 0; count < 10; ++count)
{
this.eventAggregator.GetEvent<NortificateFirmupProgressEvent>().Publish(new NortificateFirmupProgressEventEntity(count));
await Task.Delay(1000);
}
this.logger.Info("[BootFirmup] 6", nameof (BootFirmup), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 1236);
if (!this.Connect())
{
this.logger.Info("Can't connect", nameof (BootFirmup), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 1240);
return WriteToHHKBResult.CommunicationError;
}
try
{
this.logger.Info("[BootFirmup] 7", nameof (BootFirmup), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 1246);
if (!this.UpdateBootStart(firmData.FirmSize, firmData.CRC))
{
this.logger.Error("Can't start bootfirmup.", nameof (BootFirmup), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 1250);
return WriteToHHKBResult.CommunicationError;
}
this.logger.Info("[BootFirmup] 8", nameof (BootFirmup), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 1254);
if (!await this.UpdateBootSend(firmData.FirmSize, firmData.FirmRawData))
{
this.logger.Error("Can't send firmdata.", nameof (BootFirmup), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 1258);
return WriteToHHKBResult.CommunicationError;
}
this.logger.Info("[BootFirmup] 9", nameof (BootFirmup), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 1262);
if (!this.UpdateBootEnd())
{
this.logger.Error("Can't end bootfirmup.", nameof (BootFirmup), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 1266);
return WriteToHHKBResult.CommunicationError;
}
}
finally
{
this.logger.Info("[BootFirmup] 10", nameof (BootFirmup), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 1272);
this.driver.Disconnect();
this.logger.Info("[BootFirmup] 11", nameof (BootFirmup), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 1274);
}
for (count = 0; count < 10; ++count)
{
this.eventAggregator.GetEvent<NortificateFirmupProgressEvent>().Publish(new NortificateFirmupProgressEventEntity(count + 90));
await Task.Delay(1000);
}
this.logger.Info("[BootFirmup] 12", nameof (BootFirmup), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 1284);
if (!this.Connect())
{
this.logger.Info("Can't reconnect the keyboard.", nameof (BootFirmup), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 1289);
return WriteToHHKBResult.CommunicationError;
}
this.logger.Info("OK", nameof (BootFirmup), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 1293);
this.Disconnect();
return WriteToHHKBResult.Success;
}
public async Task<List<byte[]>> GetFirmware()
{
this.logger.Info("GetFirmware Start", nameof (GetFirmware), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 1306);
if (!this.Connect())
{
this.logger.Info("Can't connect any keyboards.", nameof (GetFirmware), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 1311);
return (List<byte[]>) null;
}
List<byte[]> firmwareCommand;
try
{
if (!this.NotifyApplicationState(ApplicationState.Open))
{
this.logger.Info("Can't notify Open Status.", nameof (GetFirmware), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 1319);
return (List<byte[]>) null;
}
firmwareCommand = await this.GetFirmwareCommand();
if (firmwareCommand == null)
{
this.logger.Info("Can't get firmdata.", nameof (GetFirmware), "D:\\dev\\hhkb-keymap-tool\\HHKBKeymapTool\\Models\\KeyboardDriver.cs", 1327);
return (List<byte[]>) null;
}
}
finally
{
this.Disconnect();
}
return firmwareCommand;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment