From 2afc38f7f9d2343d5a975273ef258f25effa5d4b Mon Sep 17 00:00:00 2001 From: Scott Bezek Date: Tue, 13 Jun 2023 22:45:49 -0700 Subject: [PATCH] Updated config schema, improved demo (#121) - Breaking proto updates - added position nonce to config, and added ability to specify a sub_position via config as well => more flexibility to control position via config - added version as first field of serial protocol messages, so future breaking proto updates can easily be detected and ignored - Fixed up web-based demo of a mock video editor/timeline jog/playback controller - Fixed config/state flow so that mode changes correctly maintain playback position - Demo: https://www.youtube.com/watch?v=J9192DfZplk --- firmware/src/interface_task.cpp | 24 + firmware/src/motor_task.cpp | 74 ++- firmware/src/proto_gen/smartknob.pb.c | 6 +- firmware/src/proto_gen/smartknob.pb.h | 118 +++-- firmware/src/{ => serial}/proto_helpers.h | 4 + .../src/serial/serial_protocol_protobuf.cpp | 10 +- proto/smartknob.proto | 62 ++- software/js/package-lock.json | 48 +- .../js/packages/demo-backend/package.json | 60 +-- .../js/packages/demo-backend/src/index.ts | 6 +- .../js/packages/demo-frontend/package.json | 2 +- .../js/packages/demo-frontend/src/App.tsx | 453 +++++++++++------- .../js/packages/demo-frontend/src/index.tsx | 2 +- .../js/packages/demo-frontend/src/util.ts | 2 - software/js/packages/example/package.json | 4 +- software/js/packages/example/src/index.ts | 6 +- .../packages/smartknobjs-proto/package.json | 2 +- software/js/packages/smartknobjs/package.json | 4 +- software/js/packages/smartknobjs/src/index.ts | 16 +- software/python/proto_gen/nanopb_pb2.py | 153 +++--- software/python/proto_gen/smartknob_pb2.py | 52 +- 21 files changed, 680 insertions(+), 428 deletions(-) rename firmware/src/{ => serial}/proto_helpers.h (88%) diff --git a/firmware/src/interface_task.cpp b/firmware/src/interface_task.cpp index 4be7fff..a0c7634 100644 --- a/firmware/src/interface_task.cpp +++ b/firmware/src/interface_task.cpp @@ -27,6 +27,8 @@ Adafruit_VEML7700 veml = Adafruit_VEML7700(); static PB_SmartKnobConfig configs[] = { // int32_t position; + // float sub_position_unit; + // uint8_t position_nonce; // int32_t min_position; // int32_t max_position; // float position_width_radians; @@ -39,6 +41,8 @@ static PB_SmartKnobConfig configs[] = { // float snap_point_bias; { + 0, + 0, 0, 0, -1, // max position < min position indicates no bounds @@ -54,6 +58,8 @@ static PB_SmartKnobConfig configs[] = { { 0, 0, + 1, + 0, 10, 10 * PI / 180, 0, @@ -67,6 +73,8 @@ static PB_SmartKnobConfig configs[] = { { 0, 0, + 2, + 0, 72, 10 * PI / 180, 0, @@ -80,6 +88,8 @@ static PB_SmartKnobConfig configs[] = { { 0, 0, + 3, + 0, 1, 60 * PI / 180, 1, @@ -93,6 +103,8 @@ static PB_SmartKnobConfig configs[] = { { 0, 0, + 4, + 0, 0, 60 * PI / 180, 0.01, @@ -106,6 +118,8 @@ static PB_SmartKnobConfig configs[] = { { 127, 0, + 5, + 0, 255, 1 * PI / 180, 0, @@ -119,6 +133,8 @@ static PB_SmartKnobConfig configs[] = { { 127, 0, + 5, + 0, 255, 1 * PI / 180, 1, @@ -132,6 +148,8 @@ static PB_SmartKnobConfig configs[] = { { 0, 0, + 6, + 0, 31, 8.225806452 * PI / 180, 2, @@ -145,6 +163,8 @@ static PB_SmartKnobConfig configs[] = { { 0, 0, + 6, + 0, 31, 8.225806452 * PI / 180, 0.2, @@ -158,6 +178,8 @@ static PB_SmartKnobConfig configs[] = { { 0, 0, + 7, + 0, 31, 7 * PI / 180, 2.5, @@ -170,6 +192,8 @@ static PB_SmartKnobConfig configs[] = { }, { 0, + 0, + 8, -6, 6, 60 * PI / 180, diff --git a/firmware/src/motor_task.cpp b/firmware/src/motor_task.cpp index 0363982..ccdb331 100644 --- a/firmware/src/motor_task.cpp +++ b/firmware/src/motor_task.cpp @@ -96,11 +96,15 @@ void MotorTask::run() { float current_detent_center = motor.shaft_angle; PB_SmartKnobConfig config = { .position = 0, + .sub_position_unit = 0, + .position_nonce = 0, .min_position = 0, .max_position = 1, .position_width_radians = 60 * _PI / 180, .detent_strength_unit = 0, }; + int32_t current_position = 0; + float latest_sub_position_unit = 0; float idle_check_velocity_ewma = 0; uint32_t last_idle_start = 0; @@ -118,44 +122,60 @@ void MotorTask::run() { break; case CommandType::CONFIG: { // Check new config for validity - if (command.data.config.detent_strength_unit < 0) { + PB_SmartKnobConfig& new_config = command.data.config; + if (new_config.detent_strength_unit < 0) { log("Ignoring invalid config: detent_strength_unit cannot be negative"); break; } - if (command.data.config.endstop_strength_unit < 0) { + if (new_config.endstop_strength_unit < 0) { log("Ignoring invalid config: endstop_strength_unit cannot be negative"); break; } - if (command.data.config.snap_point < 0.5) { + if (new_config.snap_point < 0.5) { log("Ignoring invalid config: snap_point must be >= 0.5 for stability"); break; } - if (command.data.config.detent_positions_count > COUNT_OF(command.data.config.detent_positions)) { + if (new_config.detent_positions_count > COUNT_OF(new_config.detent_positions)) { log("Ignoring invalid config: detent_positions_count is too large"); break; } - if (command.data.config.snap_point_bias < 0) { + if (new_config.snap_point_bias < 0) { log("Ignoring invalid config: snap_point_bias cannot be negative or there is risk of instability"); break; } // Change haptic input mode - PB_SmartKnobConfig newConfig = command.data.config; - if (newConfig.position == INT32_MIN) { - // INT32_MIN indicates no change to position, so restore from latest_config - log("maintaining position"); - newConfig.position = config.position; + bool position_updated = false; + if (new_config.position != config.position + || new_config.sub_position_unit != config.sub_position_unit + || new_config.position_nonce != config.position_nonce) { + log("applying position change"); + current_position = new_config.position; + position_updated = true; } - if (newConfig.position != config.position - || newConfig.position_width_radians != config.position_width_radians) { - // Only adjust the detent center if the position or width has changed + + if (new_config.min_position <= new_config.max_position) { + // Only check bounds if min/max indicate bounds are active (min >= max) + if (current_position < new_config.min_position) { + current_position = new_config.min_position; + log("adjusting position to min"); + } else if (current_position > new_config.max_position) { + current_position = new_config.max_position; + log("adjusting position to max"); + } + } + + if (position_updated || new_config.position_width_radians != config.position_width_radians) { log("adjusting detent center"); - current_detent_center = motor.shaft_angle; + float new_sub_position = position_updated ? new_config.sub_position_unit : latest_sub_position_unit; #if SK_INVERT_ROTATION - current_detent_center = -motor.shaft_angle; + float shaft_angle = -motor.shaft_angle; + #else + float shaft_angle = motor.shaft_angle; #endif + current_detent_center = shaft_angle + new_sub_position * new_config.position_width_radians; } - config = newConfig; + config = new_config; log("Got new config"); // Update derivative factor of torque controller based on detent width. @@ -221,26 +241,28 @@ void MotorTask::run() { float snap_point_radians = config.position_width_radians * config.snap_point; float bias_radians = config.position_width_radians * config.snap_point_bias; - float snap_point_radians_decrease = snap_point_radians + (config.position <= 0 ? bias_radians : -bias_radians); - float snap_point_radians_increase = -snap_point_radians + (config.position >= 0 ? -bias_radians : bias_radians); + float snap_point_radians_decrease = snap_point_radians + (current_position <= 0 ? bias_radians : -bias_radians); + float snap_point_radians_increase = -snap_point_radians + (current_position >= 0 ? -bias_radians : bias_radians); int32_t num_positions = config.max_position - config.min_position + 1; - if (angle_to_detent_center > snap_point_radians_decrease && (num_positions <= 0 || config.position > config.min_position)) { + if (angle_to_detent_center > snap_point_radians_decrease && (num_positions <= 0 || current_position > config.min_position)) { current_detent_center += config.position_width_radians; angle_to_detent_center -= config.position_width_radians; - config.position--; - } else if (angle_to_detent_center < snap_point_radians_increase && (num_positions <= 0 || config.position < config.max_position)) { + current_position--; + } else if (angle_to_detent_center < snap_point_radians_increase && (num_positions <= 0 || current_position < config.max_position)) { current_detent_center -= config.position_width_radians; angle_to_detent_center += config.position_width_radians; - config.position++; + current_position++; } + latest_sub_position_unit = -angle_to_detent_center / config.position_width_radians; + float dead_zone_adjustment = CLAMP( angle_to_detent_center, fmaxf(-config.position_width_radians*DEAD_ZONE_DETENT_PERCENT, -DEAD_ZONE_RAD), fminf(config.position_width_radians*DEAD_ZONE_DETENT_PERCENT, DEAD_ZONE_RAD)); - bool out_of_bounds = num_positions > 0 && ((angle_to_detent_center > 0 && config.position == config.min_position) || (angle_to_detent_center < 0 && config.position == config.max_position)); + bool out_of_bounds = num_positions > 0 && ((angle_to_detent_center > 0 && current_position == config.min_position) || (angle_to_detent_center < 0 && current_position == config.max_position)); motor.PID_velocity.limit = 10; //out_of_bounds ? 10 : 3; motor.PID_velocity.P = out_of_bounds ? config.endstop_strength_unit * 4 : config.detent_strength_unit * 4; @@ -254,7 +276,7 @@ void MotorTask::run() { if (!out_of_bounds && config.detent_positions_count > 0) { bool in_detent = false; for (uint8_t i = 0; i < config.detent_positions_count; i++) { - if (config.detent_positions[i] == config.position) { + if (config.detent_positions[i] == current_position) { in_detent = true; break; } @@ -273,8 +295,8 @@ void MotorTask::run() { // Publish current status to other registered tasks periodically if (millis() - last_publish > 5) { publish({ - .current_position = config.position, - .sub_position_unit = -angle_to_detent_center / config.position_width_radians, + .current_position = current_position, + .sub_position_unit = latest_sub_position_unit, .has_config = true, .config = config, }); diff --git a/firmware/src/proto_gen/smartknob.pb.c b/firmware/src/proto_gen/smartknob.pb.c index f5ae7aa..c67fe58 100644 --- a/firmware/src/proto_gen/smartknob.pb.c +++ b/firmware/src/proto_gen/smartknob.pb.c @@ -9,6 +9,9 @@ PB_BIND(PB_FromSmartKnob, PB_FromSmartKnob, 2) +PB_BIND(PB_ToSmartknob, PB_ToSmartknob, AUTO) + + PB_BIND(PB_Ack, PB_Ack, AUTO) @@ -18,9 +21,6 @@ PB_BIND(PB_Log, PB_Log, 2) PB_BIND(PB_SmartKnobState, PB_SmartKnobState, AUTO) -PB_BIND(PB_ToSmartknob, PB_ToSmartknob, AUTO) - - PB_BIND(PB_SmartKnobConfig, PB_SmartKnobConfig, AUTO) diff --git a/firmware/src/proto_gen/smartknob.pb.h b/firmware/src/proto_gen/smartknob.pb.h index b2de80f..7e4870e 100644 --- a/firmware/src/proto_gen/smartknob.pb.h +++ b/firmware/src/proto_gen/smartknob.pb.h @@ -20,6 +20,14 @@ typedef struct _PB_Log { typedef struct _PB_SmartKnobConfig { int32_t position; + float sub_position_unit; + /* * + Position is normally only applied when it changes, but sometimes it's desirable + to reset the position to the same value, so a nonce change can be used to force + the position values to be applied as well. + + NOTE: Must be < 256 */ + uint8_t position_nonce; int32_t min_position; int32_t max_position; float position_width_radians; @@ -39,8 +47,9 @@ typedef struct _PB_SmartKnobState { PB_SmartKnobConfig config; } PB_SmartKnobState; -/* Message FROM the SmartKnob to USB host */ +/* Message FROM the SmartKnob to the host */ typedef struct _PB_FromSmartKnob { + uint8_t protocol_version; pb_size_t which_payload; union { PB_Ack ack; @@ -53,8 +62,9 @@ typedef struct _PB_RequestState { char dummy_field; } PB_RequestState; -/* Message TO the Smartknob from the USB host */ +/* Message TO the Smartknob from the host */ typedef struct _PB_ToSmartknob { + uint8_t protocol_version; uint32_t nonce; pb_size_t which_payload; union { @@ -69,55 +79,70 @@ extern "C" { #endif /* Initializer values for message structs */ -#define PB_FromSmartKnob_init_default {0, {PB_Ack_init_default}} +#define PB_FromSmartKnob_init_default {0, 0, {PB_Ack_init_default}} +#define PB_ToSmartknob_init_default {0, 0, 0, {PB_RequestState_init_default}} #define PB_Ack_init_default {0} #define PB_Log_init_default {""} #define PB_SmartKnobState_init_default {0, 0, false, PB_SmartKnobConfig_init_default} -#define PB_ToSmartknob_init_default {0, 0, {PB_RequestState_init_default}} -#define PB_SmartKnobConfig_init_default {0, 0, 0, 0, 0, 0, 0, "", 0, {0, 0, 0, 0, 0}, 0} +#define PB_SmartKnobConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, "", 0, {0, 0, 0, 0, 0}, 0} #define PB_RequestState_init_default {0} -#define PB_FromSmartKnob_init_zero {0, {PB_Ack_init_zero}} +#define PB_FromSmartKnob_init_zero {0, 0, {PB_Ack_init_zero}} +#define PB_ToSmartknob_init_zero {0, 0, 0, {PB_RequestState_init_zero}} #define PB_Ack_init_zero {0} #define PB_Log_init_zero {""} #define PB_SmartKnobState_init_zero {0, 0, false, PB_SmartKnobConfig_init_zero} -#define PB_ToSmartknob_init_zero {0, 0, {PB_RequestState_init_zero}} -#define PB_SmartKnobConfig_init_zero {0, 0, 0, 0, 0, 0, 0, "", 0, {0, 0, 0, 0, 0}, 0} +#define PB_SmartKnobConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, "", 0, {0, 0, 0, 0, 0}, 0} #define PB_RequestState_init_zero {0} /* Field tags (for use in manual encoding/decoding) */ #define PB_Ack_nonce_tag 1 #define PB_Log_msg_tag 1 #define PB_SmartKnobConfig_position_tag 1 -#define PB_SmartKnobConfig_min_position_tag 2 -#define PB_SmartKnobConfig_max_position_tag 3 -#define PB_SmartKnobConfig_position_width_radians_tag 4 -#define PB_SmartKnobConfig_detent_strength_unit_tag 5 -#define PB_SmartKnobConfig_endstop_strength_unit_tag 6 -#define PB_SmartKnobConfig_snap_point_tag 7 -#define PB_SmartKnobConfig_text_tag 8 -#define PB_SmartKnobConfig_detent_positions_tag 9 -#define PB_SmartKnobConfig_snap_point_bias_tag 10 +#define PB_SmartKnobConfig_sub_position_unit_tag 2 +#define PB_SmartKnobConfig_position_nonce_tag 3 +#define PB_SmartKnobConfig_min_position_tag 4 +#define PB_SmartKnobConfig_max_position_tag 5 +#define PB_SmartKnobConfig_position_width_radians_tag 6 +#define PB_SmartKnobConfig_detent_strength_unit_tag 7 +#define PB_SmartKnobConfig_endstop_strength_unit_tag 8 +#define PB_SmartKnobConfig_snap_point_tag 9 +#define PB_SmartKnobConfig_text_tag 10 +#define PB_SmartKnobConfig_detent_positions_tag 11 +#define PB_SmartKnobConfig_snap_point_bias_tag 12 #define PB_SmartKnobState_current_position_tag 1 #define PB_SmartKnobState_sub_position_unit_tag 2 #define PB_SmartKnobState_config_tag 3 -#define PB_FromSmartKnob_ack_tag 1 -#define PB_FromSmartKnob_log_tag 2 -#define PB_FromSmartKnob_smartknob_state_tag 3 -#define PB_ToSmartknob_nonce_tag 1 -#define PB_ToSmartknob_request_state_tag 2 -#define PB_ToSmartknob_smartknob_config_tag 3 +#define PB_FromSmartKnob_protocol_version_tag 1 +#define PB_FromSmartKnob_ack_tag 2 +#define PB_FromSmartKnob_log_tag 3 +#define PB_FromSmartKnob_smartknob_state_tag 4 +#define PB_ToSmartknob_protocol_version_tag 1 +#define PB_ToSmartknob_nonce_tag 2 +#define PB_ToSmartknob_request_state_tag 3 +#define PB_ToSmartknob_smartknob_config_tag 4 /* Struct field encoding specification for nanopb */ #define PB_FromSmartKnob_FIELDLIST(X, a) \ -X(a, STATIC, ONEOF, MESSAGE, (payload,ack,payload.ack), 1) \ -X(a, STATIC, ONEOF, MESSAGE, (payload,log,payload.log), 2) \ -X(a, STATIC, ONEOF, MESSAGE, (payload,smartknob_state,payload.smartknob_state), 3) +X(a, STATIC, SINGULAR, UINT32, protocol_version, 1) \ +X(a, STATIC, ONEOF, MESSAGE, (payload,ack,payload.ack), 2) \ +X(a, STATIC, ONEOF, MESSAGE, (payload,log,payload.log), 3) \ +X(a, STATIC, ONEOF, MESSAGE, (payload,smartknob_state,payload.smartknob_state), 4) #define PB_FromSmartKnob_CALLBACK NULL #define PB_FromSmartKnob_DEFAULT NULL #define PB_FromSmartKnob_payload_ack_MSGTYPE PB_Ack #define PB_FromSmartKnob_payload_log_MSGTYPE PB_Log #define PB_FromSmartKnob_payload_smartknob_state_MSGTYPE PB_SmartKnobState +#define PB_ToSmartknob_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, protocol_version, 1) \ +X(a, STATIC, SINGULAR, UINT32, nonce, 2) \ +X(a, STATIC, ONEOF, MESSAGE, (payload,request_state,payload.request_state), 3) \ +X(a, STATIC, ONEOF, MESSAGE, (payload,smartknob_config,payload.smartknob_config), 4) +#define PB_ToSmartknob_CALLBACK NULL +#define PB_ToSmartknob_DEFAULT NULL +#define PB_ToSmartknob_payload_request_state_MSGTYPE PB_RequestState +#define PB_ToSmartknob_payload_smartknob_config_MSGTYPE PB_SmartKnobConfig + #define PB_Ack_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, UINT32, nonce, 1) #define PB_Ack_CALLBACK NULL @@ -136,26 +161,19 @@ X(a, STATIC, OPTIONAL, MESSAGE, config, 3) #define PB_SmartKnobState_DEFAULT NULL #define PB_SmartKnobState_config_MSGTYPE PB_SmartKnobConfig -#define PB_ToSmartknob_FIELDLIST(X, a) \ -X(a, STATIC, SINGULAR, UINT32, nonce, 1) \ -X(a, STATIC, ONEOF, MESSAGE, (payload,request_state,payload.request_state), 2) \ -X(a, STATIC, ONEOF, MESSAGE, (payload,smartknob_config,payload.smartknob_config), 3) -#define PB_ToSmartknob_CALLBACK NULL -#define PB_ToSmartknob_DEFAULT NULL -#define PB_ToSmartknob_payload_request_state_MSGTYPE PB_RequestState -#define PB_ToSmartknob_payload_smartknob_config_MSGTYPE PB_SmartKnobConfig - #define PB_SmartKnobConfig_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, INT32, position, 1) \ -X(a, STATIC, SINGULAR, INT32, min_position, 2) \ -X(a, STATIC, SINGULAR, INT32, max_position, 3) \ -X(a, STATIC, SINGULAR, FLOAT, position_width_radians, 4) \ -X(a, STATIC, SINGULAR, FLOAT, detent_strength_unit, 5) \ -X(a, STATIC, SINGULAR, FLOAT, endstop_strength_unit, 6) \ -X(a, STATIC, SINGULAR, FLOAT, snap_point, 7) \ -X(a, STATIC, SINGULAR, STRING, text, 8) \ -X(a, STATIC, REPEATED, INT32, detent_positions, 9) \ -X(a, STATIC, SINGULAR, FLOAT, snap_point_bias, 10) +X(a, STATIC, SINGULAR, FLOAT, sub_position_unit, 2) \ +X(a, STATIC, SINGULAR, UINT32, position_nonce, 3) \ +X(a, STATIC, SINGULAR, INT32, min_position, 4) \ +X(a, STATIC, SINGULAR, INT32, max_position, 5) \ +X(a, STATIC, SINGULAR, FLOAT, position_width_radians, 6) \ +X(a, STATIC, SINGULAR, FLOAT, detent_strength_unit, 7) \ +X(a, STATIC, SINGULAR, FLOAT, endstop_strength_unit, 8) \ +X(a, STATIC, SINGULAR, FLOAT, snap_point, 9) \ +X(a, STATIC, SINGULAR, STRING, text, 10) \ +X(a, STATIC, REPEATED, INT32, detent_positions, 11) \ +X(a, STATIC, SINGULAR, FLOAT, snap_point_bias, 12) #define PB_SmartKnobConfig_CALLBACK NULL #define PB_SmartKnobConfig_DEFAULT NULL @@ -165,30 +183,30 @@ X(a, STATIC, SINGULAR, FLOAT, snap_point_bias, 10) #define PB_RequestState_DEFAULT NULL extern const pb_msgdesc_t PB_FromSmartKnob_msg; +extern const pb_msgdesc_t PB_ToSmartknob_msg; extern const pb_msgdesc_t PB_Ack_msg; extern const pb_msgdesc_t PB_Log_msg; extern const pb_msgdesc_t PB_SmartKnobState_msg; -extern const pb_msgdesc_t PB_ToSmartknob_msg; extern const pb_msgdesc_t PB_SmartKnobConfig_msg; extern const pb_msgdesc_t PB_RequestState_msg; /* Defines for backwards compatibility with code written before nanopb-0.4.0 */ #define PB_FromSmartKnob_fields &PB_FromSmartKnob_msg +#define PB_ToSmartknob_fields &PB_ToSmartknob_msg #define PB_Ack_fields &PB_Ack_msg #define PB_Log_fields &PB_Log_msg #define PB_SmartKnobState_fields &PB_SmartKnobState_msg -#define PB_ToSmartknob_fields &PB_ToSmartknob_msg #define PB_SmartKnobConfig_fields &PB_SmartKnobConfig_msg #define PB_RequestState_fields &PB_RequestState_msg /* Maximum encoded size of messages (where known) */ #define PB_Ack_size 6 -#define PB_FromSmartKnob_size 261 +#define PB_FromSmartKnob_size 264 #define PB_Log_size 258 #define PB_RequestState_size 0 -#define PB_SmartKnobConfig_size 165 -#define PB_SmartKnobState_size 184 -#define PB_ToSmartknob_size 174 +#define PB_SmartKnobConfig_size 173 +#define PB_SmartKnobState_size 192 +#define PB_ToSmartknob_size 185 #ifdef __cplusplus } /* extern "C" */ diff --git a/firmware/src/proto_helpers.h b/firmware/src/serial/proto_helpers.h similarity index 88% rename from firmware/src/proto_helpers.h rename to firmware/src/serial/proto_helpers.h index d15b844..37ce1aa 100644 --- a/firmware/src/proto_helpers.h +++ b/firmware/src/serial/proto_helpers.h @@ -2,14 +2,18 @@ #include "proto_gen/smartknob.pb.h" +#define PROTOBUF_PROTOCOL_VERSION (1) + bool config_eq(PB_SmartKnobConfig& first, PB_SmartKnobConfig& second) { return first.detent_strength_unit == second.detent_strength_unit && first.endstop_strength_unit == second.endstop_strength_unit && first.position == second.position + && first.position_nonce == second.position_nonce && first.min_position == second.min_position && first.max_position == second.max_position && first.position_width_radians == second.position_width_radians && first.snap_point == second.snap_point + && first.sub_position_unit == second.sub_position_unit && strcmp(first.text, second.text) == 0 && first.detent_positions_count == second.detent_positions_count && memcmp(first.detent_positions, second.detent_positions, first.detent_positions_count * sizeof(first.detent_positions[0])) diff --git a/firmware/src/serial/serial_protocol_protobuf.cpp b/firmware/src/serial/serial_protocol_protobuf.cpp index 7e1836e..07f45df 100644 --- a/firmware/src/serial/serial_protocol_protobuf.cpp +++ b/firmware/src/serial/serial_protocol_protobuf.cpp @@ -2,7 +2,7 @@ #include "../proto_gen/smartknob.pb.h" -#include "../proto_helpers.h" +#include "proto_helpers.h" #include "crc32.h" #include "pb_encode.h" @@ -105,6 +105,13 @@ void SerialProtocolProtobuf::handlePacket(const uint8_t* buffer, size_t size) { return; } + if (pb_rx_buffer_.protocol_version != PROTOBUF_PROTOCOL_VERSION) { + char buf[200]; + snprintf(buf, sizeof(buf), "Invalid protocol version. Expected %u, received %u", PROTOBUF_PROTOCOL_VERSION, pb_rx_buffer_.protocol_version); + log(buf); + return; + } + // Always ACK immediately ack(pb_rx_buffer_.nonce); if (pb_rx_buffer_.nonce == last_nonce_) { @@ -133,6 +140,7 @@ void SerialProtocolProtobuf::handlePacket(const uint8_t* buffer, size_t size) { void SerialProtocolProtobuf::sendPbTxBuffer() { // Encode protobuf message to byte buffer pb_ostream_t stream = pb_ostream_from_buffer(tx_buffer_, sizeof(tx_buffer_)); + pb_tx_buffer_.protocol_version = PROTOBUF_PROTOCOL_VERSION; if (!pb_encode(&stream, PB_FromSmartKnob_fields, &pb_tx_buffer_)) { stream_.println(stream.errmsg); stream_.flush(); diff --git a/proto/smartknob.proto b/proto/smartknob.proto index b60bfed..7630bc2 100644 --- a/proto/smartknob.proto +++ b/proto/smartknob.proto @@ -5,13 +5,27 @@ import "nanopb.proto"; package PB; /* - * Message FROM the SmartKnob to USB host + * Message FROM the SmartKnob to the host */ message FromSmartKnob { + uint32 protocol_version = 1 [(nanopb).int_size = IS_8]; oneof payload { - Ack ack = 1; - Log log = 2; - SmartKnobState smartknob_state = 3; + Ack ack = 2; + Log log = 3; + SmartKnobState smartknob_state = 4; + } +} + +/* + * Message TO the Smartknob from the host + */ + message ToSmartknob { + uint32 protocol_version = 1 [(nanopb).int_size = IS_8]; + uint32 nonce = 2; + + oneof payload { + RequestState request_state = 3; + SmartKnobConfig smartknob_config = 4; } } @@ -29,29 +43,29 @@ message SmartKnobState { SmartKnobConfig config = 3; } -/* - * Message TO the Smartknob from the USB host - */ -message ToSmartknob { - uint32 nonce = 1; - - oneof payload { - RequestState request_state = 2; - SmartKnobConfig smartknob_config = 3; - } -} message SmartKnobConfig { int32 position = 1; - int32 min_position = 2; - int32 max_position = 3; - float position_width_radians = 4; - float detent_strength_unit = 5; - float endstop_strength_unit = 6; - float snap_point = 7; - string text = 8 [(nanopb).max_length = 50]; - repeated int32 detent_positions = 9 [(nanopb).max_count = 5]; - float snap_point_bias = 10; + float sub_position_unit = 2; + + /** + * Position is normally only applied when it changes, but sometimes it's desirable + * to reset the position to the same value, so a nonce change can be used to force + * the position values to be applied as well. + * + * NOTE: Must be < 256 + */ + uint32 position_nonce = 3 [(nanopb).int_size = IS_8]; + + int32 min_position = 4; + int32 max_position = 5; + float position_width_radians = 6; + float detent_strength_unit = 7; + float endstop_strength_unit = 8; + float snap_point = 9; + string text = 10 [(nanopb).max_length = 50]; + repeated int32 detent_positions = 11 [(nanopb).max_count = 5]; + float snap_point_bias = 12; } message RequestState {} diff --git a/software/js/package-lock.json b/software/js/package-lock.json index 6743633..52ee80a 100644 --- a/software/js/package-lock.json +++ b/software/js/package-lock.json @@ -16874,7 +16874,7 @@ "license": "Apache-2.0", "dependencies": { "serialport": "^9.2.4", - "smartknobjs": "^0.1.0", + "smartknobjs": "^1.0.0", "socket.io": "^4.5.4" }, "devDependencies": { @@ -16908,7 +16908,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-scripts": "^5.0.1", - "smartknobjs-proto": "^0.1.1", + "smartknobjs-proto": "^1.0.0", "socket.io-client": "^4.5.4", "typescript": "^4.9.3", "web-vitals": "^2.1.4" @@ -16927,7 +16927,7 @@ "license": "Apache-2.0", "dependencies": { "serialport": "^9.2.4", - "smartknobjs": "^0.1.0" + "smartknobjs": "^1.0.0" }, "devDependencies": { "@types/serialport": "^8.0.2", @@ -16940,13 +16940,13 @@ } }, "packages/smartknobjs": { - "version": "0.1.0", + "version": "1.0.0", "license": "Apache-2.0", "dependencies": { "cobs": "^0.2.1", "crc-32": "^1.2.0", "serialport": "^9.2.4", - "smartknobjs-proto": "^0.1.0" + "smartknobjs-proto": "^1.0.0" }, "devDependencies": { "@types/serialport": "^8.0.2", @@ -16959,12 +16959,40 @@ } }, "packages/smartknobjs-proto": { - "version": "0.1.1", + "version": "1.0.0", "license": "Apache-2.0", "dependencies": { "protobufjs": "^6.11.2" } }, + "packages/webserial": { + "version": "0.1.0", + "extraneous": true, + "dependencies": { + "@emotion/react": "^11.10.5", + "@emotion/styled": "^11.10.5", + "@fontsource/roboto": "^4.5.8", + "@mui/material": "^5.10.16", + "@testing-library/jest-dom": "^5.16.5", + "@testing-library/react": "^13.4.0", + "@testing-library/user-event": "^13.5.0", + "@types/jest": "^27.5.2", + "@types/node": "^16.18.4", + "@types/react": "^18.0.25", + "@types/react-dom": "^18.0.9", + "lodash": "^4.17.21", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-scripts": "^5.0.1", + "smartknobjs-proto": "^1.0.0", + "socket.io-client": "^4.5.4", + "typescript": "^4.9.3", + "web-vitals": "^2.1.4" + }, + "devDependencies": { + "@types/lodash": "^4.14.191" + } + }, "smartknobjs": { "version": "0.1.0", "extraneous": true, @@ -21779,7 +21807,7 @@ "nodemon": "^2.0.20", "prettier": "^2.4.1", "serialport": "^9.2.4", - "smartknobjs": "^0.1.0", + "smartknobjs": "^1.0.0", "socket.io": "^4.5.4", "ts-node": "^10.2.1", "typescript": "^4.9.3" @@ -21804,7 +21832,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-scripts": "^5.0.1", - "smartknobjs-proto": "^0.1.1", + "smartknobjs-proto": "^1.0.0", "socket.io-client": "^4.5.4", "typescript": "^4.9.3", "web-vitals": "^2.1.4" @@ -22793,7 +22821,7 @@ "eslint": "^8.25.0", "prettier": "^2.4.1", "serialport": "^9.2.4", - "smartknobjs": "^0.1.0", + "smartknobjs": "^1.0.0", "ts-node": "^10.2.1", "typescript": "^4.8.4" } @@ -27375,7 +27403,7 @@ "eslint": "^8.25.0", "prettier": "^2.4.1", "serialport": "^9.2.4", - "smartknobjs-proto": "^0.1.0", + "smartknobjs-proto": "^1.0.0", "ts-node": "^10.2.1", "typescript": "^4.8.4" } diff --git a/software/js/packages/demo-backend/package.json b/software/js/packages/demo-backend/package.json index 67d041f..3cf668b 100644 --- a/software/js/packages/demo-backend/package.json +++ b/software/js/packages/demo-backend/package.json @@ -1,32 +1,32 @@ { - "name": "demo-backend", - "version": "0.1.0", - "description": "", - "main": "dist/index.js", - "types": "dist/index.d.ts", - "scripts": { - "build": "tsc", - "format": "prettier --write \"**/*.+(js|ts|json)\"", - "lint": "eslint --ext .js,.ts .", - "start": "PORT=3001 ts-node src/index.ts" - }, - "author": "", - "license": "Apache-2.0", - "dependencies": { - "serialport": "^9.2.4", - "smartknobjs": "^0.1.0", - "socket.io": "^4.5.4" - }, - "devDependencies": { - "@types/express": "^4.17.14", - "@types/node": "^18.11.10", - "@types/serialport": "^8.0.2", - "@typescript-eslint/eslint-plugin": "^5.40.1", - "@typescript-eslint/parser": "^5.40.1", - "eslint": "^8.25.0", - "nodemon": "^2.0.20", - "prettier": "^2.4.1", - "ts-node": "^10.2.1", - "typescript": "^4.9.3" - } + "name": "demo-backend", + "version": "0.1.0", + "description": "", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "scripts": { + "build": "tsc", + "format": "prettier --write \"**/*.+(js|ts|json)\"", + "lint": "eslint --ext .js,.ts .", + "start": "PORT=3001 ts-node src/index.ts" + }, + "author": "", + "license": "Apache-2.0", + "dependencies": { + "serialport": "^9.2.4", + "smartknobjs": "^1.0.0", + "socket.io": "^4.5.4" + }, + "devDependencies": { + "@types/express": "^4.17.14", + "@types/node": "^18.11.10", + "@types/serialport": "^8.0.2", + "@typescript-eslint/eslint-plugin": "^5.40.1", + "@typescript-eslint/parser": "^5.40.1", + "eslint": "^8.25.0", + "nodemon": "^2.0.20", + "prettier": "^2.4.1", + "ts-node": "^10.2.1", + "typescript": "^4.9.3" + } } diff --git a/software/js/packages/demo-backend/src/index.ts b/software/js/packages/demo-backend/src/index.ts index cebd121..e820d4f 100644 --- a/software/js/packages/demo-backend/src/index.ts +++ b/software/js/packages/demo-backend/src/index.ts @@ -13,8 +13,10 @@ const start = async () => { // Implement a check for your device's vendor+product+serial // (this is more robust than the alternative of just hardcoding a "path" like "/dev/ttyUSB0") return ( - portInfo.vendorId?.toLowerCase() === '1a86'.toLowerCase() && - portInfo.productId?.toLowerCase() === '7523'.toLowerCase() + (portInfo.vendorId?.toLowerCase() === '1a86'.toLowerCase() && + portInfo.productId?.toLowerCase() === '7523'.toLowerCase()) || + (portInfo.vendorId?.toLowerCase() === '303a'.toLowerCase() && + portInfo.productId?.toLowerCase() === '1001'.toLowerCase()) // && portInfo.serialNumber === 'DEADBEEF' ) }) diff --git a/software/js/packages/demo-frontend/package.json b/software/js/packages/demo-frontend/package.json index 0edc49e..3020e0e 100644 --- a/software/js/packages/demo-frontend/package.json +++ b/software/js/packages/demo-frontend/package.json @@ -18,7 +18,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-scripts": "^5.0.1", - "smartknobjs-proto": "^0.1.1", + "smartknobjs-proto": "^1.0.0", "socket.io-client": "^4.5.4", "typescript": "^4.9.3", "web-vitals": "^2.1.4" diff --git a/software/js/packages/demo-frontend/src/App.tsx b/software/js/packages/demo-frontend/src/App.tsx index c34e409..0598d70 100644 --- a/software/js/packages/demo-frontend/src/App.tsx +++ b/software/js/packages/demo-frontend/src/App.tsx @@ -1,4 +1,4 @@ -import React, {useEffect, useMemo, useRef, useState} from 'react' +import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react' import io from 'socket.io-client' import Typography from '@mui/material/Typography' import Container from '@mui/material/Container' @@ -7,7 +7,7 @@ import ToggleButtonGroup from '@mui/material/ToggleButtonGroup' import {PB} from 'smartknobjs-proto' import {VideoInfo} from './types' import {Card, CardContent} from '@mui/material' -import {exhaustiveCheck, findNClosest, INT32_MIN, lerp, NoUndefinedField} from './util' +import {exhaustiveCheck, findNClosest, lerp, NoUndefinedField} from './util' import {groupBy, parseInt} from 'lodash' const socket = io() @@ -15,7 +15,7 @@ const socket = io() const MIN_ZOOM = 0.01 const MAX_ZOOM = 60 -const PIXELS_PER_POSITION = 10 +const PIXELS_PER_POSITION = 5 enum Mode { Scroll = 'Scroll', @@ -23,10 +23,16 @@ enum Mode { Speed = 'Speed', } -type State = { - mode: Mode - playbackSpeed: number +type PlaybackState = { + speed: number currentFrame: number +} + +type InterfaceState = { + zoomTimelinePixelsPerFrame: number +} + +type Config = NoUndefinedField & { zoomTimelinePixelsPerFrame: number } @@ -35,197 +41,283 @@ export type AppProps = { } export const App: React.FC = ({info}) => { const [isConnected, setIsConnected] = useState(socket.connected) - const [state, setState] = useState>( + + const [smartKnobState, setSmartKnobState] = useState>( PB.SmartKnobState.toObject(PB.SmartKnobState.create({config: PB.SmartKnobConfig.create()}), { defaults: true, }) as NoUndefinedField, ) - const [derivedState, setDerivedState] = useState({ - mode: Mode.Scroll, - playbackSpeed: 0, + const [smartKnobConfig, setSmartKnobConfig] = useState({ + position: 0, + subPositionUnit: 0, + positionNonce: Math.floor(Math.random() * 255), + minPosition: 0, + maxPosition: 0, + positionWidthRadians: (15 * Math.PI) / 180, + detentStrengthUnit: 0, + endstopStrengthUnit: 1, + snapPoint: 0.7, + text: Mode.Scroll, + detentPositions: [], + snapPointBias: 0, + + zoomTimelinePixelsPerFrame: 0.1, + }) + useEffect(() => { + console.log('send config', smartKnobConfig) + socket.emit('set_config', smartKnobConfig) + }, [ + smartKnobConfig.position, + smartKnobConfig.subPositionUnit, + smartKnobConfig.positionNonce, + smartKnobConfig.minPosition, + smartKnobConfig.maxPosition, + smartKnobConfig.positionWidthRadians, + smartKnobConfig.detentStrengthUnit, + smartKnobConfig.endstopStrengthUnit, + smartKnobConfig.snapPoint, + smartKnobConfig.text, + smartKnobConfig.detentPositions, + smartKnobConfig.snapPointBias, + ]) + const [playbackState, setPlaybackState] = useState({ + speed: 0, currentFrame: 0, + }) + const [interfaceState, setInterfaceState] = useState({ zoomTimelinePixelsPerFrame: 0.1, }) - useMemo(() => { - setDerivedState((cur) => { - const modeText = state.config.text - if (modeText === Mode.Scroll) { - const rawFrame = Math.trunc( - ((state.currentPosition + state.subPositionUnit) * PIXELS_PER_POSITION) / - cur.zoomTimelinePixelsPerFrame, - ) - return { - mode: Mode.Scroll, - playbackSpeed: 0, - currentFrame: Math.min(Math.max(rawFrame, 0), info.totalFrames - 1), - zoomTimelinePixelsPerFrame: cur.zoomTimelinePixelsPerFrame, - } - } else if (modeText === Mode.Frames) { - return { - mode: Mode.Frames, - playbackSpeed: 0, - currentFrame: state.currentPosition ?? 0, - zoomTimelinePixelsPerFrame: cur.zoomTimelinePixelsPerFrame, - } - } else if (modeText === Mode.Speed) { - const normalizedWholeValue = state.currentPosition - const normalizedFractional = - Math.sign(state.subPositionUnit) * - lerp(state.subPositionUnit * Math.sign(state.subPositionUnit), 0.1, 0.9, 0, 1) - const normalized = normalizedWholeValue + normalizedFractional - const speed = Math.sign(normalized) * Math.pow(2, Math.abs(normalized) - 1) - return { - mode: Mode.Speed, - playbackSpeed: speed, - currentFrame: cur.currentFrame, - zoomTimelinePixelsPerFrame: cur.zoomTimelinePixelsPerFrame, - } - } - return cur - }) - }, [state.config.text, state.currentPosition, state.subPositionUnit]) - - const totalPositions = Math.ceil((info.totalFrames * derivedState.zoomTimelinePixelsPerFrame) / PIXELS_PER_POSITION) + const totalPositions = Math.ceil( + (info.totalFrames * smartKnobConfig.zoomTimelinePixelsPerFrame) / PIXELS_PER_POSITION, + ) const detentPositions = useMemo(() => { // Always include the first and last positions at detents const positionsToFrames = groupBy([0, ...info.boundaryFrames, info.totalFrames - 1], (frame) => - Math.round((frame * derivedState.zoomTimelinePixelsPerFrame) / PIXELS_PER_POSITION), + Math.round((frame * smartKnobConfig.zoomTimelinePixelsPerFrame) / PIXELS_PER_POSITION), ) console.log(JSON.stringify(positionsToFrames)) return positionsToFrames - }, [info.boundaryFrames, totalPositions, derivedState.zoomTimelinePixelsPerFrame]) + }, [info.boundaryFrames, info.totalFrames, totalPositions, smartKnobConfig.zoomTimelinePixelsPerFrame]) - // Continuous config updates for scrolling, to update detent positions - useMemo(() => { - if (derivedState.mode === Mode.Scroll) { - const config = PB.SmartKnobConfig.create({ - position: INT32_MIN, - minPosition: 0, - maxPosition: totalPositions - 1, - positionWidthRadians: (8 * Math.PI) / 180, - detentStrengthUnit: 2.5, - endstopStrengthUnit: 1, - snapPoint: 0.7, - text: Mode.Scroll, - detentPositions: findNClosest(Object.keys(detentPositions).map(parseInt), state.currentPosition, 5), - snapPointBias: 0, - }) - socket.emit('set_config', config) - } - }, [derivedState.mode, derivedState.zoomTimelinePixelsPerFrame, detentPositions, state.currentPosition]) + const scrollPositionWholeMemo = useMemo(() => { + const position = (playbackState.currentFrame * smartKnobConfig.zoomTimelinePixelsPerFrame) / PIXELS_PER_POSITION + return Math.round(position) + }, [playbackState.currentFrame, smartKnobConfig.zoomTimelinePixelsPerFrame]) + const nClosestMemo = useMemo(() => { + return findNClosest(Object.keys(detentPositions).map(parseInt), scrollPositionWholeMemo, 5).sort( + (a, b) => a - b, + ) + }, [scrollPositionWholeMemo]) - // For one-off config pushes, e.g. mode changes - const pushConfig = (state: State) => { - let config: PB.SmartKnobConfig - if (state.mode === Mode.Scroll) { - const position = Math.trunc((state.currentFrame * state.zoomTimelinePixelsPerFrame) / PIXELS_PER_POSITION) - config = PB.SmartKnobConfig.create({ - position, - minPosition: 0, - maxPosition: Math.trunc( - ((info.totalFrames - 1) * state.zoomTimelinePixelsPerFrame) / PIXELS_PER_POSITION, - ), - positionWidthRadians: (8 * Math.PI) / 180, - detentStrengthUnit: 2.5, - endstopStrengthUnit: 1, - snapPoint: 0.7, - text: Mode.Scroll, - detentPositions: findNClosest(Object.keys(detentPositions).map(parseInt), position, 5), - snapPointBias: 0, - }) - } else if (state.mode === Mode.Frames) { - config = PB.SmartKnobConfig.create({ - position: state.currentFrame, - minPosition: 0, - maxPosition: info.totalFrames - 1, - positionWidthRadians: (1.5 * Math.PI) / 180, - detentStrengthUnit: 1, - endstopStrengthUnit: 1, - snapPoint: 1.1, - text: Mode.Frames, - detentPositions: [], - snapPointBias: 0, - }) - } else if (state.mode === Mode.Speed) { - config = PB.SmartKnobConfig.create({ - position: state.playbackSpeed === 0 ? 0 : INT32_MIN, - minPosition: state.currentFrame === 0 ? 0 : -6, - maxPosition: state.currentFrame === info.totalFrames - 1 ? 0 : 6, - positionWidthRadians: (60 * Math.PI) / 180, - detentStrengthUnit: 1, - endstopStrengthUnit: 1, - snapPoint: 0.55, - text: Mode.Speed, - detentPositions: [], - snapPointBias: 0.4, - }) - } else { - throw exhaustiveCheck(state.mode) - } - socket.emit('set_config', config) - } + const changeMode = useCallback( + (newMode: Mode) => { + if (newMode === Mode.Scroll) { + setSmartKnobConfig((curConfig) => { + const position = + (playbackState.currentFrame * curConfig.zoomTimelinePixelsPerFrame) / PIXELS_PER_POSITION + const positionWhole = Math.round(position) + const subPositionUnit = position - positionWhole + return { + position, + subPositionUnit, + positionNonce: (curConfig.positionNonce + 1) % 256, + minPosition: 0, + maxPosition: Math.trunc( + ((info.totalFrames - 1) * curConfig.zoomTimelinePixelsPerFrame) / PIXELS_PER_POSITION, + ), + positionWidthRadians: (8 * Math.PI) / 180, + detentStrengthUnit: 3, + endstopStrengthUnit: 1, + snapPoint: 0.7, + text: Mode.Scroll, + detentPositions: findNClosest(Object.keys(detentPositions).map(parseInt), position, 5), + snapPointBias: 0, - const setCurrentFrame = (fn: (oldFrame: number) => number) => { - setDerivedState((cur) => { - const newState = {...cur} - if (cur.mode === Mode.Speed) { - newState.currentFrame = fn(cur.currentFrame) - } - return newState - }) - } - - // Timer for speed-based playback - useEffect(() => { - const refreshInterval = 20 - const fps = info.frameRate * derivedState.playbackSpeed - if (derivedState.mode === Mode.Speed && fps !== 0) { - const timer = setInterval(() => { - setCurrentFrame((oldFrame) => { - const newFrame = oldFrame + (fps * refreshInterval) / 1000 - - const oldFrameTrunc = Math.trunc(oldFrame) - const newFrameTrunc = Math.trunc(newFrame) - - if (newFrame < 0 || newFrame >= info.totalFrames) { - const clampedNewFrame = Math.min(Math.max(newFrame, 0), info.totalFrames - 1) - if (oldFrame !== clampedNewFrame) { - // If we've hit a boundary, push a config to set the bounds - pushConfig({ - mode: Mode.Speed, - playbackSpeed: 0, - currentFrame: Math.trunc(clampedNewFrame), - zoomTimelinePixelsPerFrame: derivedState.zoomTimelinePixelsPerFrame, - }) - } - return clampedNewFrame - } else { - if ( - (oldFrameTrunc === 0 && newFrameTrunc > 0) || - (oldFrameTrunc === info.totalFrames - 1 && newFrameTrunc < info.totalFrames - 1) - ) { - // If we've left a boundary condition, push a config to reset the bounds - pushConfig({ - mode: derivedState.mode, - playbackSpeed: 0, - currentFrame: newFrameTrunc, - zoomTimelinePixelsPerFrame: derivedState.zoomTimelinePixelsPerFrame, - }) - } - return newFrame + zoomTimelinePixelsPerFrame: curConfig.zoomTimelinePixelsPerFrame, } }) + } else if (newMode === Mode.Frames) { + setSmartKnobConfig((curConfig) => { + return { + position: playbackState.currentFrame, + subPositionUnit: 0, + positionNonce: (curConfig.positionNonce + 1) % 256, + minPosition: 0, + maxPosition: info.totalFrames - 1, + positionWidthRadians: (1.8 * Math.PI) / 180, + detentStrengthUnit: 1, + endstopStrengthUnit: 1, + snapPoint: 1.1, + text: Mode.Frames, + detentPositions: [], + snapPointBias: 0, + + zoomTimelinePixelsPerFrame: curConfig.zoomTimelinePixelsPerFrame, + } + }) + } else if (newMode === Mode.Speed) { + setSmartKnobConfig((curConfig) => { + return { + position: 0, + subPositionUnit: 0, + positionNonce: (curConfig.positionNonce + 1) % 256, + minPosition: playbackState.currentFrame === 0 ? 0 : -6, + maxPosition: playbackState.currentFrame === info.totalFrames - 1 ? 0 : 6, + positionWidthRadians: (60 * Math.PI) / 180, + detentStrengthUnit: 1, + endstopStrengthUnit: 1, + snapPoint: 0.55, + text: Mode.Speed, + detentPositions: [], + snapPointBias: 0.4, + + zoomTimelinePixelsPerFrame: curConfig.zoomTimelinePixelsPerFrame, + } + }) + } else { + exhaustiveCheck(newMode) + } + }, + [detentPositions, info.totalFrames, playbackState], + ) + + useEffect(() => { + if (smartKnobState.config.text === '') { + console.debug('No valid state yet') + return + } + + const currentMode = smartKnobState.config.text as Mode + if (currentMode !== smartKnobConfig.text) { + console.debug('Mode mismatch, ignoring state', {configMode: smartKnobConfig.text, stateMode: currentMode}) + return + } + + // Update playbackState + if (currentMode === Mode.Scroll) { + // TODO: round input based on zoom level to avoid noise + const rawFrame = Math.trunc( + ((smartKnobState.currentPosition + smartKnobState.subPositionUnit) * PIXELS_PER_POSITION) / + smartKnobConfig.zoomTimelinePixelsPerFrame, + ) + const frame = + detentPositions[smartKnobState.currentPosition]?.[0] ?? + Math.min(Math.max(rawFrame, 0), info.totalFrames - 1) + setPlaybackState({ + speed: 0, + currentFrame: frame, + }) + + // Update config with N nearest detents + setSmartKnobConfig((curConfig) => { + let positionInfo: Partial = {} + if (interfaceState.zoomTimelinePixelsPerFrame !== curConfig.zoomTimelinePixelsPerFrame) { + const position = + (playbackState.currentFrame * interfaceState.zoomTimelinePixelsPerFrame) / PIXELS_PER_POSITION + const positionWhole = Math.round(position) + const subPositionUnit = position - positionWhole + positionInfo = { + position, + subPositionUnit, + positionNonce: (curConfig.positionNonce + 1) % 256, + minPosition: 0, + maxPosition: Math.trunc( + ((info.totalFrames - 1) * interfaceState.zoomTimelinePixelsPerFrame) / PIXELS_PER_POSITION, + ), + zoomTimelinePixelsPerFrame: interfaceState.zoomTimelinePixelsPerFrame, + } + } + return { + ...curConfig, + ...positionInfo, + detentPositions: nClosestMemo, + } + }) + } else if (currentMode === Mode.Frames) { + setPlaybackState({ + speed: 0, + currentFrame: smartKnobState.currentPosition, + }) + // No config updates needed + } else if (currentMode === Mode.Speed) { + const normalizedWholeValue = smartKnobState.currentPosition + const normalizedFractional = + Math.sign(smartKnobState.subPositionUnit) * + lerp(smartKnobState.subPositionUnit * Math.sign(smartKnobState.subPositionUnit), 0.1, 0.9, 0, 1) + const normalized = normalizedWholeValue + normalizedFractional + const speed = Math.sign(normalized) * Math.pow(2, Math.abs(normalized) - 1) + const roundedSpeed = Math.trunc(speed * 10) / 10 + setPlaybackState((cur) => { + return { + speed: roundedSpeed, + currentFrame: cur.currentFrame, + } + }) + + // Update config with bounds depending on current frame + setSmartKnobConfig((curConfig) => { + return { + ...curConfig, + minPosition: playbackState.currentFrame === 0 ? 0 : -6, + maxPosition: playbackState.currentFrame === info.totalFrames - 1 ? 0 : 6, + } + }) + } else { + exhaustiveCheck(currentMode) + } + }, [ + detentPositions, + nClosestMemo, + info.totalFrames, + smartKnobState.config.text, + smartKnobState.currentPosition, + smartKnobState.subPositionUnit, + smartKnobConfig.text, + playbackState.currentFrame, + playbackState.speed, + interfaceState.zoomTimelinePixelsPerFrame, + ]) + + const refreshInterval = 20 + const updateFrame = useCallback(() => { + const fps = info.frameRate * playbackState.speed + setPlaybackState((cur) => { + const newFrame = cur.currentFrame + (fps * refreshInterval) / 1000 + const clampedNewFrame = Math.min(Math.max(newFrame, 0), info.totalFrames - 1) + return { + speed: cur.speed, + currentFrame: clampedNewFrame, + } + }) + }, [info.frameRate, playbackState.speed]) + + // Store the latest callback in a ref so the long-lived interval closure can invoke the latest version. + // See https://overreacted.io/making-setinterval-declarative-with-react-hooks/ for more + const savedCallback = useRef<() => void | null>() + useEffect(() => { + savedCallback.current = updateFrame + }, [updateFrame]) + + const isPlaying = useMemo(() => { + return playbackState.speed !== 0 + }, [playbackState.speed]) + + useEffect(() => { + if (smartKnobState.config.text === Mode.Speed && isPlaying) { + const timer = setInterval(() => { + if (savedCallback.current) { + savedCallback.current() + } }, refreshInterval) return () => clearInterval(timer) } - }, [derivedState.mode, derivedState.playbackSpeed, info.totalFrames, info.frameRate]) + }, [smartKnobState.config.text, isPlaying]) // Socket.io subscription useEffect(() => { socket.on('connect', () => { setIsConnected(true) - pushConfig(derivedState) }) socket.on('disconnect', () => { @@ -237,7 +329,7 @@ export const App: React.FC = ({info}) => { const stateObj = PB.SmartKnobState.toObject(state, { defaults: true, }) as NoUndefinedField - setState(stateObj) + setSmartKnobState(stateObj) }) return () => { socket.off('connect') @@ -260,16 +352,13 @@ export const App: React.FC = ({info}) => { )} { if (value === null) { return } - pushConfig({ - ...derivedState, - mode: value, - }) + changeMode(value) }} aria-label="Mode" > @@ -280,18 +369,18 @@ export const App: React.FC = ({info}) => { ))} - Frame {Math.trunc(derivedState.currentFrame)} / {info.totalFrames - 1} + Frame {Math.trunc(playbackState.currentFrame)} / {info.totalFrames - 1}
- Speed {Math.trunc(derivedState.playbackSpeed * 10) / 10} + Speed {playbackState.speed}
{ - setDerivedState((cur) => { + setInterfaceState((cur) => { const newZoom = Math.min( Math.max(cur.zoomTimelinePixelsPerFrame * factor, MIN_ZOOM), MAX_ZOOM, @@ -306,7 +395,7 @@ export const App: React.FC = ({info}) => { /> -
{JSON.stringify(detentPositions)}
+
{JSON.stringify(smartKnobConfig)}
diff --git a/software/js/packages/demo-frontend/src/index.tsx b/software/js/packages/demo-frontend/src/index.tsx index 3d045ec..26956b3 100644 --- a/software/js/packages/demo-frontend/src/index.tsx +++ b/software/js/packages/demo-frontend/src/index.tsx @@ -17,7 +17,7 @@ const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement) const info: VideoInfo = { totalFrames: 30 * 60 * 5, frameRate: 30, - boundaryFrames: [312, 400, 1234, 1290, 3000, 4000], + boundaryFrames: [312, 400, 1234, 1290, 3000, 3300, 4000, 8000, 8100], } root.render( diff --git a/software/js/packages/demo-frontend/src/util.ts b/software/js/packages/demo-frontend/src/util.ts index 3ce2473..9bba99d 100644 --- a/software/js/packages/demo-frontend/src/util.ts +++ b/software/js/packages/demo-frontend/src/util.ts @@ -18,8 +18,6 @@ export type NoUndefinedField = { [P in keyof T]-?: NoUndefinedField> } -export const INT32_MIN = -2147483648 - export function findNClosest(numbers: number[], target: number, n: number): number[] { // First, we sort the numbers in ascending order based on their absolute difference // from the target number. This means that the numbers that are closest to the target diff --git a/software/js/packages/example/package.json b/software/js/packages/example/package.json index 7467493..c39f905 100644 --- a/software/js/packages/example/package.json +++ b/software/js/packages/example/package.json @@ -1,7 +1,7 @@ { "name": "example", "version": "0.1.0", - "description": "SmartKnob Interface Library", + "description": "SmartKnob Interface Example", "main": "dist/index.js", "types": "dist/index.d.ts", "scripts": { @@ -14,7 +14,7 @@ "license": "Apache-2.0", "dependencies": { "serialport": "^9.2.4", - "smartknobjs": "^0.1.0" + "smartknobjs": "^1.0.0" }, "devDependencies": { "@types/serialport": "^8.0.2", diff --git a/software/js/packages/example/src/index.ts b/software/js/packages/example/src/index.ts index 2d6f519..5d9f2b8 100644 --- a/software/js/packages/example/src/index.ts +++ b/software/js/packages/example/src/index.ts @@ -30,9 +30,11 @@ const main = async () => { if (message.payload === 'log' && message.log) { console.log('LOG', message.log.msg) } else if (message.payload === 'smartknobState' && message.smartknobState) { + // Only log if it's a significant change (major position change, or at least 5 degrees) + const radianChange = (message.smartknobState.subPositionUnit ?? 0) * (message.smartknobState.config?.positionWidthRadians ?? 0) - (lastLoggedState?.subPositionUnit ?? 0) * (lastLoggedState?.config?.positionWidthRadians ?? 0) if ( message.smartknobState.currentPosition !== lastLoggedState?.currentPosition || - Math.abs((message.smartknobState.subPositionUnit ?? 0) - (lastLoggedState?.subPositionUnit ?? 0)) > 1 + Math.abs(radianChange)*180/Math.PI > 5 ) { console.log( `State:\n${JSON.stringify( @@ -50,6 +52,8 @@ const main = async () => { detentStrengthUnit: 1, endstopStrengthUnit: 1, position: 0, + subPositionUnit: 0, + positionNonce: Math.floor(Math.random()*255), // Pick a random nonce to force a position reset on start minPosition: 0, maxPosition: 4, positionWidthRadians: (10 * Math.PI) / 180, diff --git a/software/js/packages/smartknobjs-proto/package.json b/software/js/packages/smartknobjs-proto/package.json index 09a3c84..e6f5537 100644 --- a/software/js/packages/smartknobjs-proto/package.json +++ b/software/js/packages/smartknobjs-proto/package.json @@ -1,6 +1,6 @@ { "name": "smartknobjs-proto", - "version": "0.1.1", + "version": "1.0.0", "description": "SmartKnob Protobuf Generated Code", "main": "dist/smartknob_proto.js", "types": "dist/smartknob_proto.d.ts", diff --git a/software/js/packages/smartknobjs/package.json b/software/js/packages/smartknobjs/package.json index 1626975..69eb0e1 100644 --- a/software/js/packages/smartknobjs/package.json +++ b/software/js/packages/smartknobjs/package.json @@ -1,6 +1,6 @@ { "name": "smartknobjs", - "version": "0.1.0", + "version": "1.0.0", "description": "SmartKnob Interface Library", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -15,7 +15,7 @@ "cobs": "^0.2.1", "crc-32": "^1.2.0", "serialport": "^9.2.4", - "smartknobjs-proto": "^0.1.0" + "smartknobjs-proto": "^1.0.0" }, "devDependencies": { "@types/serialport": "^8.0.2", diff --git a/software/js/packages/smartknobjs/src/index.ts b/software/js/packages/smartknobjs/src/index.ts index 45ec943..d09e3d4 100644 --- a/software/js/packages/smartknobjs/src/index.ts +++ b/software/js/packages/smartknobjs/src/index.ts @@ -1,9 +1,11 @@ import SerialPort = require('serialport') -import {decode, encode} from 'cobs' +import {decode as cobsDecode, encode as cobsEncode} from 'cobs' import * as CRC32 from 'crc-32' import {PB} from 'smartknobjs-proto' +const PROTOBUF_PROTOCOL_VERSION = 1 + export type MessageCallback = (message: PB.FromSmartKnob) => void type QueueEntry = { @@ -83,7 +85,7 @@ export class SmartKnob { // Iterate 0-delimited packets while ((i = this.buffer.indexOf(0)) != -1) { const raw_buffer = this.buffer.slice(0, i) - const packet = decode(raw_buffer) as Buffer + const packet = cobsDecode(raw_buffer) as Buffer this.buffer = this.buffer.slice(i + 1) if (packet.length <= 4) { console.debug(`Received short packet ${this.buffer.slice(0, i)}`) @@ -109,6 +111,13 @@ export class SmartKnob { return } + if (message.protocolVersion !== PROTOBUF_PROTOCOL_VERSION) { + console.warn( + `Invalid protocol version. Expected ${PROTOBUF_PROTOCOL_VERSION}, received ${message.protocolVersion}`, + ) + return + } + if (message.payload === 'ack') { const nonce = message.ack?.nonce ?? undefined if (nonce === undefined) { @@ -126,6 +135,7 @@ export class SmartKnob { if (this.port === null) { return } + message.protocolVersion = PROTOBUF_PROTOCOL_VERSION message.nonce = this.lastNonce++ // Encode before enqueueing to ensure messages don't change once they're queued @@ -173,7 +183,7 @@ export class SmartKnob { const crcBuffer = Buffer.from([crc & 0xff, (crc >>> 8) & 0xff, (crc >>> 16) & 0xff, (crc >>> 24) & 0xff]) const packet = Buffer.concat([payload, crcBuffer]) - const encodedDelimitedPacket = Buffer.concat([encode(packet), Buffer.from([0])]) + const encodedDelimitedPacket = Buffer.concat([cobsEncode(packet), Buffer.from([0])]) this.retryTimeout = setTimeout(() => { this.retryTimeout = null diff --git a/software/python/proto_gen/nanopb_pb2.py b/software/python/proto_gen/nanopb_pb2.py index f7ffb32..58cede8 100644 --- a/software/python/proto_gen/nanopb_pb2.py +++ b/software/python/proto_gen/nanopb_pb2.py @@ -1,8 +1,7 @@ +# -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # source: nanopb.proto -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) from google.protobuf.internal import enum_type_wrapper from google.protobuf import descriptor as _descriptor from google.protobuf import message as _message @@ -20,8 +19,9 @@ DESCRIPTOR = _descriptor.FileDescriptor( name='nanopb.proto', package='', syntax='proto2', - serialized_options=_b('\n\030fi.kapsi.koti.jpa.nanopb'), - serialized_pb=_b('\n\x0cnanopb.proto\x1a google/protobuf/descriptor.proto\"\xa4\x07\n\rNanoPBOptions\x12\x10\n\x08max_size\x18\x01 \x01(\x05\x12\x12\n\nmax_length\x18\x0e \x01(\x05\x12\x11\n\tmax_count\x18\x02 \x01(\x05\x12&\n\x08int_size\x18\x07 \x01(\x0e\x32\x08.IntSize:\nIS_DEFAULT\x12$\n\x04type\x18\x03 \x01(\x0e\x32\n.FieldType:\nFT_DEFAULT\x12\x18\n\nlong_names\x18\x04 \x01(\x08:\x04true\x12\x1c\n\rpacked_struct\x18\x05 \x01(\x08:\x05\x66\x61lse\x12\x1a\n\x0bpacked_enum\x18\n \x01(\x08:\x05\x66\x61lse\x12\x1b\n\x0cskip_message\x18\x06 \x01(\x08:\x05\x66\x61lse\x12\x18\n\tno_unions\x18\x08 \x01(\x08:\x05\x66\x61lse\x12\r\n\x05msgid\x18\t \x01(\r\x12\x1e\n\x0f\x61nonymous_oneof\x18\x0b \x01(\x08:\x05\x66\x61lse\x12\x15\n\x06proto3\x18\x0c \x01(\x08:\x05\x66\x61lse\x12#\n\x14proto3_singular_msgs\x18\x15 \x01(\x08:\x05\x66\x61lse\x12\x1d\n\x0e\x65num_to_string\x18\r \x01(\x08:\x05\x66\x61lse\x12\x1b\n\x0c\x66ixed_length\x18\x0f \x01(\x08:\x05\x66\x61lse\x12\x1a\n\x0b\x66ixed_count\x18\x10 \x01(\x08:\x05\x66\x61lse\x12\x1e\n\x0fsubmsg_callback\x18\x16 \x01(\x08:\x05\x66\x61lse\x12/\n\x0cmangle_names\x18\x11 \x01(\x0e\x32\x11.TypenameMangling:\x06M_NONE\x12(\n\x11\x63\x61llback_datatype\x18\x12 \x01(\t:\rpb_callback_t\x12\x34\n\x11\x63\x61llback_function\x18\x13 \x01(\t:\x19pb_default_field_callback\x12\x30\n\x0e\x64\x65scriptorsize\x18\x14 \x01(\x0e\x32\x0f.DescriptorSize:\x07\x44S_AUTO\x12\x1a\n\x0b\x64\x65\x66\x61ult_has\x18\x17 \x01(\x08:\x05\x66\x61lse\x12\x0f\n\x07include\x18\x18 \x03(\t\x12\x0f\n\x07\x65xclude\x18\x1a \x03(\t\x12\x0f\n\x07package\x18\x19 \x01(\t\x12\x41\n\rtype_override\x18\x1b \x01(\x0e\x32*.google.protobuf.FieldDescriptorProto.Type\x12\x19\n\x0bsort_by_tag\x18\x1c \x01(\x08:\x04true\x12.\n\rfallback_type\x18\x1d \x01(\x0e\x32\n.FieldType:\x0b\x46T_CALLBACK*i\n\tFieldType\x12\x0e\n\nFT_DEFAULT\x10\x00\x12\x0f\n\x0b\x46T_CALLBACK\x10\x01\x12\x0e\n\nFT_POINTER\x10\x04\x12\r\n\tFT_STATIC\x10\x02\x12\r\n\tFT_IGNORE\x10\x03\x12\r\n\tFT_INLINE\x10\x05*D\n\x07IntSize\x12\x0e\n\nIS_DEFAULT\x10\x00\x12\x08\n\x04IS_8\x10\x08\x12\t\n\x05IS_16\x10\x10\x12\t\n\x05IS_32\x10 \x12\t\n\x05IS_64\x10@*Z\n\x10TypenameMangling\x12\n\n\x06M_NONE\x10\x00\x12\x13\n\x0fM_STRIP_PACKAGE\x10\x01\x12\r\n\tM_FLATTEN\x10\x02\x12\x16\n\x12M_PACKAGE_INITIALS\x10\x03*E\n\x0e\x44\x65scriptorSize\x12\x0b\n\x07\x44S_AUTO\x10\x00\x12\x08\n\x04\x44S_1\x10\x01\x12\x08\n\x04\x44S_2\x10\x02\x12\x08\n\x04\x44S_4\x10\x04\x12\x08\n\x04\x44S_8\x10\x08:E\n\x0enanopb_fileopt\x12\x1c.google.protobuf.FileOptions\x18\xf2\x07 \x01(\x0b\x32\x0e.NanoPBOptions:G\n\rnanopb_msgopt\x12\x1f.google.protobuf.MessageOptions\x18\xf2\x07 \x01(\x0b\x32\x0e.NanoPBOptions:E\n\x0enanopb_enumopt\x12\x1c.google.protobuf.EnumOptions\x18\xf2\x07 \x01(\x0b\x32\x0e.NanoPBOptions:>\n\x06nanopb\x12\x1d.google.protobuf.FieldOptions\x18\xf2\x07 \x01(\x0b\x32\x0e.NanoPBOptionsB\x1a\n\x18\x66i.kapsi.koti.jpa.nanopb') + serialized_options=b'\n\030fi.kapsi.koti.jpa.nanopb', + create_key=_descriptor._internal_create_key, + serialized_pb=b'\n\x0cnanopb.proto\x1a google/protobuf/descriptor.proto\"\xa4\x07\n\rNanoPBOptions\x12\x10\n\x08max_size\x18\x01 \x01(\x05\x12\x12\n\nmax_length\x18\x0e \x01(\x05\x12\x11\n\tmax_count\x18\x02 \x01(\x05\x12&\n\x08int_size\x18\x07 \x01(\x0e\x32\x08.IntSize:\nIS_DEFAULT\x12$\n\x04type\x18\x03 \x01(\x0e\x32\n.FieldType:\nFT_DEFAULT\x12\x18\n\nlong_names\x18\x04 \x01(\x08:\x04true\x12\x1c\n\rpacked_struct\x18\x05 \x01(\x08:\x05\x66\x61lse\x12\x1a\n\x0bpacked_enum\x18\n \x01(\x08:\x05\x66\x61lse\x12\x1b\n\x0cskip_message\x18\x06 \x01(\x08:\x05\x66\x61lse\x12\x18\n\tno_unions\x18\x08 \x01(\x08:\x05\x66\x61lse\x12\r\n\x05msgid\x18\t \x01(\r\x12\x1e\n\x0f\x61nonymous_oneof\x18\x0b \x01(\x08:\x05\x66\x61lse\x12\x15\n\x06proto3\x18\x0c \x01(\x08:\x05\x66\x61lse\x12#\n\x14proto3_singular_msgs\x18\x15 \x01(\x08:\x05\x66\x61lse\x12\x1d\n\x0e\x65num_to_string\x18\r \x01(\x08:\x05\x66\x61lse\x12\x1b\n\x0c\x66ixed_length\x18\x0f \x01(\x08:\x05\x66\x61lse\x12\x1a\n\x0b\x66ixed_count\x18\x10 \x01(\x08:\x05\x66\x61lse\x12\x1e\n\x0fsubmsg_callback\x18\x16 \x01(\x08:\x05\x66\x61lse\x12/\n\x0cmangle_names\x18\x11 \x01(\x0e\x32\x11.TypenameMangling:\x06M_NONE\x12(\n\x11\x63\x61llback_datatype\x18\x12 \x01(\t:\rpb_callback_t\x12\x34\n\x11\x63\x61llback_function\x18\x13 \x01(\t:\x19pb_default_field_callback\x12\x30\n\x0e\x64\x65scriptorsize\x18\x14 \x01(\x0e\x32\x0f.DescriptorSize:\x07\x44S_AUTO\x12\x1a\n\x0b\x64\x65\x66\x61ult_has\x18\x17 \x01(\x08:\x05\x66\x61lse\x12\x0f\n\x07include\x18\x18 \x03(\t\x12\x0f\n\x07\x65xclude\x18\x1a \x03(\t\x12\x0f\n\x07package\x18\x19 \x01(\t\x12\x41\n\rtype_override\x18\x1b \x01(\x0e\x32*.google.protobuf.FieldDescriptorProto.Type\x12\x19\n\x0bsort_by_tag\x18\x1c \x01(\x08:\x04true\x12.\n\rfallback_type\x18\x1d \x01(\x0e\x32\n.FieldType:\x0b\x46T_CALLBACK*i\n\tFieldType\x12\x0e\n\nFT_DEFAULT\x10\x00\x12\x0f\n\x0b\x46T_CALLBACK\x10\x01\x12\x0e\n\nFT_POINTER\x10\x04\x12\r\n\tFT_STATIC\x10\x02\x12\r\n\tFT_IGNORE\x10\x03\x12\r\n\tFT_INLINE\x10\x05*D\n\x07IntSize\x12\x0e\n\nIS_DEFAULT\x10\x00\x12\x08\n\x04IS_8\x10\x08\x12\t\n\x05IS_16\x10\x10\x12\t\n\x05IS_32\x10 \x12\t\n\x05IS_64\x10@*Z\n\x10TypenameMangling\x12\n\n\x06M_NONE\x10\x00\x12\x13\n\x0fM_STRIP_PACKAGE\x10\x01\x12\r\n\tM_FLATTEN\x10\x02\x12\x16\n\x12M_PACKAGE_INITIALS\x10\x03*E\n\x0e\x44\x65scriptorSize\x12\x0b\n\x07\x44S_AUTO\x10\x00\x12\x08\n\x04\x44S_1\x10\x01\x12\x08\n\x04\x44S_2\x10\x02\x12\x08\n\x04\x44S_4\x10\x04\x12\x08\n\x04\x44S_8\x10\x08:E\n\x0enanopb_fileopt\x12\x1c.google.protobuf.FileOptions\x18\xf2\x07 \x01(\x0b\x32\x0e.NanoPBOptions:G\n\rnanopb_msgopt\x12\x1f.google.protobuf.MessageOptions\x18\xf2\x07 \x01(\x0b\x32\x0e.NanoPBOptions:E\n\x0enanopb_enumopt\x12\x1c.google.protobuf.EnumOptions\x18\xf2\x07 \x01(\x0b\x32\x0e.NanoPBOptions:>\n\x06nanopb\x12\x1d.google.protobuf.FieldOptions\x18\xf2\x07 \x01(\x0b\x32\x0e.NanoPBOptionsB\x1a\n\x18\x66i.kapsi.koti.jpa.nanopb' , dependencies=[google_dot_protobuf_dot_descriptor__pb2.DESCRIPTOR,]) @@ -30,31 +30,38 @@ _FIELDTYPE = _descriptor.EnumDescriptor( full_name='FieldType', filename=None, file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, values=[ _descriptor.EnumValueDescriptor( name='FT_DEFAULT', index=0, number=0, serialized_options=None, - type=None), + type=None, + create_key=_descriptor._internal_create_key), _descriptor.EnumValueDescriptor( name='FT_CALLBACK', index=1, number=1, serialized_options=None, - type=None), + type=None, + create_key=_descriptor._internal_create_key), _descriptor.EnumValueDescriptor( name='FT_POINTER', index=2, number=4, serialized_options=None, - type=None), + type=None, + create_key=_descriptor._internal_create_key), _descriptor.EnumValueDescriptor( name='FT_STATIC', index=3, number=2, serialized_options=None, - type=None), + type=None, + create_key=_descriptor._internal_create_key), _descriptor.EnumValueDescriptor( name='FT_IGNORE', index=4, number=3, serialized_options=None, - type=None), + type=None, + create_key=_descriptor._internal_create_key), _descriptor.EnumValueDescriptor( name='FT_INLINE', index=5, number=5, serialized_options=None, - type=None), + type=None, + create_key=_descriptor._internal_create_key), ], containing_type=None, serialized_options=None, @@ -69,27 +76,33 @@ _INTSIZE = _descriptor.EnumDescriptor( full_name='IntSize', filename=None, file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, values=[ _descriptor.EnumValueDescriptor( name='IS_DEFAULT', index=0, number=0, serialized_options=None, - type=None), + type=None, + create_key=_descriptor._internal_create_key), _descriptor.EnumValueDescriptor( name='IS_8', index=1, number=8, serialized_options=None, - type=None), + type=None, + create_key=_descriptor._internal_create_key), _descriptor.EnumValueDescriptor( name='IS_16', index=2, number=16, serialized_options=None, - type=None), + type=None, + create_key=_descriptor._internal_create_key), _descriptor.EnumValueDescriptor( name='IS_32', index=3, number=32, serialized_options=None, - type=None), + type=None, + create_key=_descriptor._internal_create_key), _descriptor.EnumValueDescriptor( name='IS_64', index=4, number=64, serialized_options=None, - type=None), + type=None, + create_key=_descriptor._internal_create_key), ], containing_type=None, serialized_options=None, @@ -104,23 +117,28 @@ _TYPENAMEMANGLING = _descriptor.EnumDescriptor( full_name='TypenameMangling', filename=None, file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, values=[ _descriptor.EnumValueDescriptor( name='M_NONE', index=0, number=0, serialized_options=None, - type=None), + type=None, + create_key=_descriptor._internal_create_key), _descriptor.EnumValueDescriptor( name='M_STRIP_PACKAGE', index=1, number=1, serialized_options=None, - type=None), + type=None, + create_key=_descriptor._internal_create_key), _descriptor.EnumValueDescriptor( name='M_FLATTEN', index=2, number=2, serialized_options=None, - type=None), + type=None, + create_key=_descriptor._internal_create_key), _descriptor.EnumValueDescriptor( name='M_PACKAGE_INITIALS', index=3, number=3, serialized_options=None, - type=None), + type=None, + create_key=_descriptor._internal_create_key), ], containing_type=None, serialized_options=None, @@ -135,27 +153,33 @@ _DESCRIPTORSIZE = _descriptor.EnumDescriptor( full_name='DescriptorSize', filename=None, file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, values=[ _descriptor.EnumValueDescriptor( name='DS_AUTO', index=0, number=0, serialized_options=None, - type=None), + type=None, + create_key=_descriptor._internal_create_key), _descriptor.EnumValueDescriptor( name='DS_1', index=1, number=1, serialized_options=None, - type=None), + type=None, + create_key=_descriptor._internal_create_key), _descriptor.EnumValueDescriptor( name='DS_2', index=2, number=2, serialized_options=None, - type=None), + type=None, + create_key=_descriptor._internal_create_key), _descriptor.EnumValueDescriptor( name='DS_4', index=3, number=4, serialized_options=None, - type=None), + type=None, + create_key=_descriptor._internal_create_key), _descriptor.EnumValueDescriptor( name='DS_8', index=4, number=8, serialized_options=None, - type=None), + type=None, + create_key=_descriptor._internal_create_key), ], containing_type=None, serialized_options=None, @@ -193,7 +217,7 @@ nanopb_fileopt = _descriptor.FieldDescriptor( has_default_value=False, default_value=None, message_type=None, enum_type=None, containing_type=None, is_extension=True, extension_scope=None, - serialized_options=None, file=DESCRIPTOR) + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key) NANOPB_MSGOPT_FIELD_NUMBER = 1010 nanopb_msgopt = _descriptor.FieldDescriptor( name='nanopb_msgopt', full_name='nanopb_msgopt', index=1, @@ -201,7 +225,7 @@ nanopb_msgopt = _descriptor.FieldDescriptor( has_default_value=False, default_value=None, message_type=None, enum_type=None, containing_type=None, is_extension=True, extension_scope=None, - serialized_options=None, file=DESCRIPTOR) + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key) NANOPB_ENUMOPT_FIELD_NUMBER = 1010 nanopb_enumopt = _descriptor.FieldDescriptor( name='nanopb_enumopt', full_name='nanopb_enumopt', index=2, @@ -209,7 +233,7 @@ nanopb_enumopt = _descriptor.FieldDescriptor( has_default_value=False, default_value=None, message_type=None, enum_type=None, containing_type=None, is_extension=True, extension_scope=None, - serialized_options=None, file=DESCRIPTOR) + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key) NANOPB_FIELD_NUMBER = 1010 nanopb = _descriptor.FieldDescriptor( name='nanopb', full_name='nanopb', index=3, @@ -217,7 +241,7 @@ nanopb = _descriptor.FieldDescriptor( has_default_value=False, default_value=None, message_type=None, enum_type=None, containing_type=None, is_extension=True, extension_scope=None, - serialized_options=None, file=DESCRIPTOR) + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key) _NANOPBOPTIONS = _descriptor.Descriptor( @@ -226,6 +250,7 @@ _NANOPBOPTIONS = _descriptor.Descriptor( filename=None, file=DESCRIPTOR, containing_type=None, + create_key=_descriptor._internal_create_key, fields=[ _descriptor.FieldDescriptor( name='max_size', full_name='NanoPBOptions.max_size', index=0, @@ -233,203 +258,203 @@ _NANOPBOPTIONS = _descriptor.Descriptor( has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), _descriptor.FieldDescriptor( name='max_length', full_name='NanoPBOptions.max_length', index=1, number=14, type=5, cpp_type=1, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), _descriptor.FieldDescriptor( name='max_count', full_name='NanoPBOptions.max_count', index=2, number=2, type=5, cpp_type=1, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), _descriptor.FieldDescriptor( name='int_size', full_name='NanoPBOptions.int_size', index=3, number=7, type=14, cpp_type=8, label=1, has_default_value=True, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), _descriptor.FieldDescriptor( name='type', full_name='NanoPBOptions.type', index=4, number=3, type=14, cpp_type=8, label=1, has_default_value=True, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), _descriptor.FieldDescriptor( name='long_names', full_name='NanoPBOptions.long_names', index=5, number=4, type=8, cpp_type=7, label=1, has_default_value=True, default_value=True, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), _descriptor.FieldDescriptor( name='packed_struct', full_name='NanoPBOptions.packed_struct', index=6, number=5, type=8, cpp_type=7, label=1, has_default_value=True, default_value=False, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), _descriptor.FieldDescriptor( name='packed_enum', full_name='NanoPBOptions.packed_enum', index=7, number=10, type=8, cpp_type=7, label=1, has_default_value=True, default_value=False, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), _descriptor.FieldDescriptor( name='skip_message', full_name='NanoPBOptions.skip_message', index=8, number=6, type=8, cpp_type=7, label=1, has_default_value=True, default_value=False, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), _descriptor.FieldDescriptor( name='no_unions', full_name='NanoPBOptions.no_unions', index=9, number=8, type=8, cpp_type=7, label=1, has_default_value=True, default_value=False, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), _descriptor.FieldDescriptor( name='msgid', full_name='NanoPBOptions.msgid', index=10, number=9, type=13, cpp_type=3, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), _descriptor.FieldDescriptor( name='anonymous_oneof', full_name='NanoPBOptions.anonymous_oneof', index=11, number=11, type=8, cpp_type=7, label=1, has_default_value=True, default_value=False, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), _descriptor.FieldDescriptor( name='proto3', full_name='NanoPBOptions.proto3', index=12, number=12, type=8, cpp_type=7, label=1, has_default_value=True, default_value=False, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), _descriptor.FieldDescriptor( name='proto3_singular_msgs', full_name='NanoPBOptions.proto3_singular_msgs', index=13, number=21, type=8, cpp_type=7, label=1, has_default_value=True, default_value=False, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), _descriptor.FieldDescriptor( name='enum_to_string', full_name='NanoPBOptions.enum_to_string', index=14, number=13, type=8, cpp_type=7, label=1, has_default_value=True, default_value=False, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), _descriptor.FieldDescriptor( name='fixed_length', full_name='NanoPBOptions.fixed_length', index=15, number=15, type=8, cpp_type=7, label=1, has_default_value=True, default_value=False, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), _descriptor.FieldDescriptor( name='fixed_count', full_name='NanoPBOptions.fixed_count', index=16, number=16, type=8, cpp_type=7, label=1, has_default_value=True, default_value=False, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), _descriptor.FieldDescriptor( name='submsg_callback', full_name='NanoPBOptions.submsg_callback', index=17, number=22, type=8, cpp_type=7, label=1, has_default_value=True, default_value=False, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), _descriptor.FieldDescriptor( name='mangle_names', full_name='NanoPBOptions.mangle_names', index=18, number=17, type=14, cpp_type=8, label=1, has_default_value=True, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), _descriptor.FieldDescriptor( name='callback_datatype', full_name='NanoPBOptions.callback_datatype', index=19, number=18, type=9, cpp_type=9, label=1, - has_default_value=True, default_value=_b("pb_callback_t").decode('utf-8'), + has_default_value=True, default_value=b"pb_callback_t".decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), _descriptor.FieldDescriptor( name='callback_function', full_name='NanoPBOptions.callback_function', index=20, number=19, type=9, cpp_type=9, label=1, - has_default_value=True, default_value=_b("pb_default_field_callback").decode('utf-8'), + has_default_value=True, default_value=b"pb_default_field_callback".decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), _descriptor.FieldDescriptor( name='descriptorsize', full_name='NanoPBOptions.descriptorsize', index=21, number=20, type=14, cpp_type=8, label=1, has_default_value=True, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), _descriptor.FieldDescriptor( name='default_has', full_name='NanoPBOptions.default_has', index=22, number=23, type=8, cpp_type=7, label=1, has_default_value=True, default_value=False, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), _descriptor.FieldDescriptor( name='include', full_name='NanoPBOptions.include', index=23, number=24, type=9, cpp_type=9, label=3, has_default_value=False, default_value=[], message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), _descriptor.FieldDescriptor( name='exclude', full_name='NanoPBOptions.exclude', index=24, number=26, type=9, cpp_type=9, label=3, has_default_value=False, default_value=[], message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), _descriptor.FieldDescriptor( name='package', full_name='NanoPBOptions.package', index=25, number=25, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), + has_default_value=False, default_value=b"".decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), _descriptor.FieldDescriptor( name='type_override', full_name='NanoPBOptions.type_override', index=26, number=27, type=14, cpp_type=8, label=1, has_default_value=False, default_value=1, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), _descriptor.FieldDescriptor( name='sort_by_tag', full_name='NanoPBOptions.sort_by_tag', index=27, number=28, type=8, cpp_type=7, label=1, has_default_value=True, default_value=True, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), _descriptor.FieldDescriptor( name='fallback_type', full_name='NanoPBOptions.fallback_type', index=28, number=29, type=14, cpp_type=8, label=1, has_default_value=True, default_value=1, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), ], extensions=[ ], @@ -463,11 +488,11 @@ DESCRIPTOR.extensions_by_name['nanopb_enumopt'] = nanopb_enumopt DESCRIPTOR.extensions_by_name['nanopb'] = nanopb _sym_db.RegisterFileDescriptor(DESCRIPTOR) -NanoPBOptions = _reflection.GeneratedProtocolMessageType('NanoPBOptions', (_message.Message,), dict( - DESCRIPTOR = _NANOPBOPTIONS, - __module__ = 'nanopb_pb2' +NanoPBOptions = _reflection.GeneratedProtocolMessageType('NanoPBOptions', (_message.Message,), { + 'DESCRIPTOR' : _NANOPBOPTIONS, + '__module__' : 'nanopb_pb2' # @@protoc_insertion_point(class_scope:NanoPBOptions) - )) + }) _sym_db.RegisterMessage(NanoPBOptions) nanopb_fileopt.message_type = _NANOPBOPTIONS diff --git a/software/python/proto_gen/smartknob_pb2.py b/software/python/proto_gen/smartknob_pb2.py index 17f3643..466b17b 100644 --- a/software/python/proto_gen/smartknob_pb2.py +++ b/software/python/proto_gen/smartknob_pb2.py @@ -15,15 +15,15 @@ _sym_db = _symbol_database.Default() import nanopb_pb2 as nanopb__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0fsmartknob.proto\x12\x02PB\x1a\x0cnanopb.proto\"y\n\rFromSmartKnob\x12\x16\n\x03\x61\x63k\x18\x01 \x01(\x0b\x32\x07.PB.AckH\x00\x12\x16\n\x03log\x18\x02 \x01(\x0b\x32\x07.PB.LogH\x00\x12-\n\x0fsmartknob_state\x18\x03 \x01(\x0b\x32\x12.PB.SmartKnobStateH\x00\x42\t\n\x07payload\"\x14\n\x03\x41\x63k\x12\r\n\x05nonce\x18\x01 \x01(\r\"\x1a\n\x03Log\x12\x13\n\x03msg\x18\x01 \x01(\tB\x06\x92?\x03p\xff\x01\"j\n\x0eSmartKnobState\x12\x18\n\x10\x63urrent_position\x18\x01 \x01(\x05\x12\x19\n\x11sub_position_unit\x18\x02 \x01(\x02\x12#\n\x06\x63onfig\x18\x03 \x01(\x0b\x32\x13.PB.SmartKnobConfig\"\x83\x01\n\x0bToSmartknob\x12\r\n\x05nonce\x18\x01 \x01(\r\x12)\n\rrequest_state\x18\x02 \x01(\x0b\x32\x10.PB.RequestStateH\x00\x12/\n\x10smartknob_config\x18\x03 \x01(\x0b\x32\x13.PB.SmartKnobConfigH\x00\x42\t\n\x07payload\"\x8f\x02\n\x0fSmartKnobConfig\x12\x10\n\x08position\x18\x01 \x01(\x05\x12\x14\n\x0cmin_position\x18\x02 \x01(\x05\x12\x14\n\x0cmax_position\x18\x03 \x01(\x05\x12\x1e\n\x16position_width_radians\x18\x04 \x01(\x02\x12\x1c\n\x14\x64\x65tent_strength_unit\x18\x05 \x01(\x02\x12\x1d\n\x15\x65ndstop_strength_unit\x18\x06 \x01(\x02\x12\x12\n\nsnap_point\x18\x07 \x01(\x02\x12\x13\n\x04text\x18\x08 \x01(\tB\x05\x92?\x02p2\x12\x1f\n\x10\x64\x65tent_positions\x18\t \x03(\x05\x42\x05\x92?\x02\x10\x05\x12\x17\n\x0fsnap_point_bias\x18\n \x01(\x02\"\x0e\n\x0cRequestStateb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0fsmartknob.proto\x12\x02PB\x1a\x0cnanopb.proto\"\x9a\x01\n\rFromSmartKnob\x12\x1f\n\x10protocol_version\x18\x01 \x01(\rB\x05\x92?\x02\x38\x08\x12\x16\n\x03\x61\x63k\x18\x02 \x01(\x0b\x32\x07.PB.AckH\x00\x12\x16\n\x03log\x18\x03 \x01(\x0b\x32\x07.PB.LogH\x00\x12-\n\x0fsmartknob_state\x18\x04 \x01(\x0b\x32\x12.PB.SmartKnobStateH\x00\x42\t\n\x07payload\"\xa4\x01\n\x0bToSmartknob\x12\x1f\n\x10protocol_version\x18\x01 \x01(\rB\x05\x92?\x02\x38\x08\x12\r\n\x05nonce\x18\x02 \x01(\r\x12)\n\rrequest_state\x18\x03 \x01(\x0b\x32\x10.PB.RequestStateH\x00\x12/\n\x10smartknob_config\x18\x04 \x01(\x0b\x32\x13.PB.SmartKnobConfigH\x00\x42\t\n\x07payload\"\x14\n\x03\x41\x63k\x12\r\n\x05nonce\x18\x01 \x01(\r\"\x1a\n\x03Log\x12\x13\n\x03msg\x18\x01 \x01(\tB\x06\x92?\x03p\xff\x01\"j\n\x0eSmartKnobState\x12\x18\n\x10\x63urrent_position\x18\x01 \x01(\x05\x12\x19\n\x11sub_position_unit\x18\x02 \x01(\x02\x12#\n\x06\x63onfig\x18\x03 \x01(\x0b\x32\x13.PB.SmartKnobConfig\"\xc9\x02\n\x0fSmartKnobConfig\x12\x10\n\x08position\x18\x01 \x01(\x05\x12\x19\n\x11sub_position_unit\x18\x02 \x01(\x02\x12\x1d\n\x0eposition_nonce\x18\x03 \x01(\rB\x05\x92?\x02\x38\x08\x12\x14\n\x0cmin_position\x18\x04 \x01(\x05\x12\x14\n\x0cmax_position\x18\x05 \x01(\x05\x12\x1e\n\x16position_width_radians\x18\x06 \x01(\x02\x12\x1c\n\x14\x64\x65tent_strength_unit\x18\x07 \x01(\x02\x12\x1d\n\x15\x65ndstop_strength_unit\x18\x08 \x01(\x02\x12\x12\n\nsnap_point\x18\t \x01(\x02\x12\x13\n\x04text\x18\n \x01(\tB\x05\x92?\x02p2\x12\x1f\n\x10\x64\x65tent_positions\x18\x0b \x03(\x05\x42\x05\x92?\x02\x10\x05\x12\x17\n\x0fsnap_point_bias\x18\x0c \x01(\x02\"\x0e\n\x0cRequestStateb\x06proto3') _FROMSMARTKNOB = DESCRIPTOR.message_types_by_name['FromSmartKnob'] +_TOSMARTKNOB = DESCRIPTOR.message_types_by_name['ToSmartknob'] _ACK = DESCRIPTOR.message_types_by_name['Ack'] _LOG = DESCRIPTOR.message_types_by_name['Log'] _SMARTKNOBSTATE = DESCRIPTOR.message_types_by_name['SmartKnobState'] -_TOSMARTKNOB = DESCRIPTOR.message_types_by_name['ToSmartknob'] _SMARTKNOBCONFIG = DESCRIPTOR.message_types_by_name['SmartKnobConfig'] _REQUESTSTATE = DESCRIPTOR.message_types_by_name['RequestState'] FromSmartKnob = _reflection.GeneratedProtocolMessageType('FromSmartKnob', (_message.Message,), { @@ -33,6 +33,13 @@ FromSmartKnob = _reflection.GeneratedProtocolMessageType('FromSmartKnob', (_mess }) _sym_db.RegisterMessage(FromSmartKnob) +ToSmartknob = _reflection.GeneratedProtocolMessageType('ToSmartknob', (_message.Message,), { + 'DESCRIPTOR' : _TOSMARTKNOB, + '__module__' : 'smartknob_pb2' + # @@protoc_insertion_point(class_scope:PB.ToSmartknob) + }) +_sym_db.RegisterMessage(ToSmartknob) + Ack = _reflection.GeneratedProtocolMessageType('Ack', (_message.Message,), { 'DESCRIPTOR' : _ACK, '__module__' : 'smartknob_pb2' @@ -54,13 +61,6 @@ SmartKnobState = _reflection.GeneratedProtocolMessageType('SmartKnobState', (_me }) _sym_db.RegisterMessage(SmartKnobState) -ToSmartknob = _reflection.GeneratedProtocolMessageType('ToSmartknob', (_message.Message,), { - 'DESCRIPTOR' : _TOSMARTKNOB, - '__module__' : 'smartknob_pb2' - # @@protoc_insertion_point(class_scope:PB.ToSmartknob) - }) -_sym_db.RegisterMessage(ToSmartknob) - SmartKnobConfig = _reflection.GeneratedProtocolMessageType('SmartKnobConfig', (_message.Message,), { 'DESCRIPTOR' : _SMARTKNOBCONFIG, '__module__' : 'smartknob_pb2' @@ -78,24 +78,30 @@ _sym_db.RegisterMessage(RequestState) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None + _FROMSMARTKNOB.fields_by_name['protocol_version']._options = None + _FROMSMARTKNOB.fields_by_name['protocol_version']._serialized_options = b'\222?\0028\010' + _TOSMARTKNOB.fields_by_name['protocol_version']._options = None + _TOSMARTKNOB.fields_by_name['protocol_version']._serialized_options = b'\222?\0028\010' _LOG.fields_by_name['msg']._options = None _LOG.fields_by_name['msg']._serialized_options = b'\222?\003p\377\001' + _SMARTKNOBCONFIG.fields_by_name['position_nonce']._options = None + _SMARTKNOBCONFIG.fields_by_name['position_nonce']._serialized_options = b'\222?\0028\010' _SMARTKNOBCONFIG.fields_by_name['text']._options = None _SMARTKNOBCONFIG.fields_by_name['text']._serialized_options = b'\222?\002p2' _SMARTKNOBCONFIG.fields_by_name['detent_positions']._options = None _SMARTKNOBCONFIG.fields_by_name['detent_positions']._serialized_options = b'\222?\002\020\005' - _FROMSMARTKNOB._serialized_start=37 - _FROMSMARTKNOB._serialized_end=158 - _ACK._serialized_start=160 - _ACK._serialized_end=180 - _LOG._serialized_start=182 - _LOG._serialized_end=208 - _SMARTKNOBSTATE._serialized_start=210 - _SMARTKNOBSTATE._serialized_end=316 - _TOSMARTKNOB._serialized_start=319 - _TOSMARTKNOB._serialized_end=450 - _SMARTKNOBCONFIG._serialized_start=453 - _SMARTKNOBCONFIG._serialized_end=724 - _REQUESTSTATE._serialized_start=726 - _REQUESTSTATE._serialized_end=740 + _FROMSMARTKNOB._serialized_start=38 + _FROMSMARTKNOB._serialized_end=192 + _TOSMARTKNOB._serialized_start=195 + _TOSMARTKNOB._serialized_end=359 + _ACK._serialized_start=361 + _ACK._serialized_end=381 + _LOG._serialized_start=383 + _LOG._serialized_end=409 + _SMARTKNOBSTATE._serialized_start=411 + _SMARTKNOBSTATE._serialized_end=517 + _SMARTKNOBCONFIG._serialized_start=520 + _SMARTKNOBCONFIG._serialized_end=849 + _REQUESTSTATE._serialized_start=851 + _REQUESTSTATE._serialized_end=865 # @@protoc_insertion_point(module_scope)