hamlib 0.4.1 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/hamlib.cpp CHANGED
@@ -40,6 +40,23 @@ struct RigListData {
40
40
  Napi::Env env;
41
41
  };
42
42
 
43
+ struct RigConfigFieldDescriptor {
44
+ int token;
45
+ std::string name;
46
+ std::string label;
47
+ std::string tooltip;
48
+ std::string defaultValue;
49
+ int type;
50
+ double numericMin;
51
+ double numericMax;
52
+ double numericStep;
53
+ std::vector<std::string> options;
54
+ };
55
+
56
+ struct RigConfigSchemaData {
57
+ std::vector<RigConfigFieldDescriptor> fields;
58
+ };
59
+
43
60
  using namespace Napi;
44
61
 
45
62
  Napi::FunctionReference NodeHamLib::constructor;
@@ -55,6 +72,31 @@ static std::string publicVfoToken(int vfo) {
55
72
  return rawToken;
56
73
  }
57
74
 
75
+ static const char* publicConfTypeName(int type) {
76
+ switch (type) {
77
+ case 0:
78
+ return "string";
79
+ case 1:
80
+ return "combo";
81
+ case 2:
82
+ return "numeric";
83
+ case 3:
84
+ return "checkbutton";
85
+ case 4:
86
+ return "button";
87
+ case 5:
88
+ return "binary";
89
+ case 6:
90
+ return "int";
91
+ default:
92
+ return "unknown";
93
+ }
94
+ }
95
+
96
+ static bool hasPositiveValue(int value) {
97
+ return value > 0;
98
+ }
99
+
58
100
  static int parseVfoString(Napi::Env env, const std::string& vfoToken) {
59
101
  int vfo = shim_rig_parse_vfo(vfoToken.c_str());
60
102
  if (vfo == SHIM_RIG_VFO_NONE) {
@@ -64,6 +106,27 @@ static int parseVfoString(Napi::Env env, const std::string& vfoToken) {
64
106
  return vfo;
65
107
  }
66
108
 
109
+ static int antennaOrdinalToMask(Napi::Env env, int antennaOrdinal) {
110
+ if (antennaOrdinal < 1 || antennaOrdinal > 30) {
111
+ Napi::RangeError::New(env, "Antenna number must be between 1 and 30").ThrowAsJavaScriptException();
112
+ return 0;
113
+ }
114
+ return static_cast<int>(1u << (antennaOrdinal - 1));
115
+ }
116
+
117
+ static int antennaMaskToOrdinal(int antennaMask) {
118
+ const uint32_t mask = static_cast<uint32_t>(antennaMask);
119
+ if (mask == 0 || (mask & (mask - 1)) != 0) {
120
+ return 0;
121
+ }
122
+ for (int bit = 0; bit < 30; ++bit) {
123
+ if (mask == (1u << bit)) {
124
+ return bit + 1;
125
+ }
126
+ }
127
+ return 0;
128
+ }
129
+
67
130
  // Base AsyncWorker implementation with Promise support
68
131
  HamLibAsyncWorker::HamLibAsyncWorker(Napi::Env env, NodeHamLib* hamlib_instance)
69
132
  : AsyncWorker(env), hamlib_instance_(hamlib_instance), result_code_(0), error_message_(""), deferred_(Napi::Promise::Deferred::New(env)) {}
@@ -1188,9 +1251,9 @@ public:
1188
1251
  } else {
1189
1252
  Napi::Object result = Napi::Object::New(env);
1190
1253
 
1191
- result.Set("currentAntenna", Napi::Number::New(env, antenna_curr_));
1192
- result.Set("txAntenna", Napi::Number::New(env, antenna_tx_));
1193
- result.Set("rxAntenna", Napi::Number::New(env, antenna_rx_));
1254
+ result.Set("currentAntenna", Napi::Number::New(env, antennaMaskToOrdinal(antenna_curr_)));
1255
+ result.Set("txAntenna", Napi::Number::New(env, antennaMaskToOrdinal(antenna_tx_)));
1256
+ result.Set("rxAntenna", Napi::Number::New(env, antennaMaskToOrdinal(antenna_rx_)));
1194
1257
  result.Set("option", Napi::Number::New(env, option_));
1195
1258
 
1196
1259
  deferred_.Resolve(result);
@@ -3726,18 +3789,39 @@ Napi::Value NodeHamLib::SetAntenna(const Napi::CallbackInfo & info) {
3726
3789
  return env.Null();
3727
3790
  }
3728
3791
 
3729
- int antenna = info[0].As<Napi::Number>().Int32Value();
3730
-
3731
- // Support optional VFO parameter: setAntenna(antenna) or setAntenna(antenna, vfo)
3732
- int vfo = SHIM_RIG_VFO_CURR;
3733
- if (info.Length() >= 2 && info[1].IsString()) {
3734
- vfo = parseVfoParameter(info, 1, SHIM_RIG_VFO_CURR);
3735
- RETURN_NULL_IF_INVALID_VFO(vfo);
3792
+ const int antennaOrdinal = info[0].As<Napi::Number>().Int32Value();
3793
+ const int antenna = antennaOrdinalToMask(env, antennaOrdinal);
3794
+ if (antenna == 0) {
3795
+ return env.Null();
3736
3796
  }
3737
-
3738
- // Default option value (can be extended later if needed)
3797
+
3798
+ int vfo = SHIM_RIG_VFO_CURR;
3739
3799
  float option = 0.0f;
3740
3800
 
3801
+ if (info.Length() >= 2) {
3802
+ if (info[1].IsNumber()) {
3803
+ option = info[1].As<Napi::Number>().FloatValue();
3804
+ if (info.Length() >= 3 && info[2].IsString()) {
3805
+ vfo = parseVfoParameter(info, 2, SHIM_RIG_VFO_CURR);
3806
+ RETURN_NULL_IF_INVALID_VFO(vfo);
3807
+ } else if (info.Length() >= 3 && !info[2].IsUndefined()) {
3808
+ Napi::TypeError::New(env, "Expected VFO token as third argument").ThrowAsJavaScriptException();
3809
+ return env.Null();
3810
+ }
3811
+ } else if (info[1].IsString()) {
3812
+ vfo = parseVfoParameter(info, 1, SHIM_RIG_VFO_CURR);
3813
+ RETURN_NULL_IF_INVALID_VFO(vfo);
3814
+ } else if (!info[1].IsUndefined()) {
3815
+ Napi::TypeError::New(env, "Expected antenna option as number or VFO token as string").ThrowAsJavaScriptException();
3816
+ return env.Null();
3817
+ }
3818
+ }
3819
+
3820
+ if (info.Length() >= 3 && info[1].IsString()) {
3821
+ Napi::TypeError::New(env, "Antenna option must come before VFO").ThrowAsJavaScriptException();
3822
+ return env.Null();
3823
+ }
3824
+
3741
3825
  SetAntennaAsyncWorker* asyncWorker = new SetAntennaAsyncWorker(env, this, antenna, vfo, option);
3742
3826
  asyncWorker->Queue();
3743
3827
  return asyncWorker->GetPromise();
@@ -3832,6 +3916,8 @@ Napi::Function NodeHamLib::GetClass(Napi::Env env) {
3832
3916
  NodeHamLib::InstanceMethod("setDcdType", & NodeHamLib::SetDcdType),
3833
3917
  NodeHamLib::InstanceMethod("getDcdType", & NodeHamLib::GetDcdType),
3834
3918
  NodeHamLib::InstanceMethod("getSupportedSerialConfigs", & NodeHamLib::GetSupportedSerialConfigs),
3919
+ NodeHamLib::InstanceMethod("getConfigSchema", & NodeHamLib::GetConfigSchema),
3920
+ NodeHamLib::InstanceMethod("getPortCaps", & NodeHamLib::GetPortCaps),
3835
3921
 
3836
3922
  // Power Control
3837
3923
  NodeHamLib::InstanceMethod("setPowerstat", & NodeHamLib::SetPowerstat),
@@ -4220,7 +4306,7 @@ Napi::Value NodeHamLib::GetSupportedSerialConfigs(const Napi::CallbackInfo& info
4220
4306
  pttTypeOptions[5u] = Napi::String::New(env, "GPIO");
4221
4307
  pttTypeOptions[6u] = Napi::String::New(env, "GPION");
4222
4308
  pttTypeOptions[7u] = Napi::String::New(env, "NONE");
4223
- configs.Set("intype", pttTypeOptions);
4309
+ configs.Set("ptt_type", pttTypeOptions);
4224
4310
 
4225
4311
  // DCD type options
4226
4312
  Napi::Array dcdTypeOptions = Napi::Array::New(env, 9);
@@ -4233,11 +4319,124 @@ Napi::Value NodeHamLib::GetSupportedSerialConfigs(const Napi::CallbackInfo& info
4233
4319
  dcdTypeOptions[6u] = Napi::String::New(env, "GPIO");
4234
4320
  dcdTypeOptions[7u] = Napi::String::New(env, "GPION");
4235
4321
  dcdTypeOptions[8u] = Napi::String::New(env, "NONE");
4236
- configs.Set("intype", dcdTypeOptions);
4322
+ configs.Set("dcd_type", dcdTypeOptions);
4237
4323
 
4238
4324
  return configs;
4239
4325
  }
4240
4326
 
4327
+ int NodeHamLib::rig_config_callback(const shim_confparam_info_t* info, void* data) {
4328
+ RigConfigSchemaData* schema_data = static_cast<RigConfigSchemaData*>(data);
4329
+
4330
+ if (!schema_data || !info) {
4331
+ return 0;
4332
+ }
4333
+
4334
+ RigConfigFieldDescriptor field;
4335
+ field.token = info->token;
4336
+ field.name = info->name;
4337
+ field.label = info->label;
4338
+ field.tooltip = info->tooltip;
4339
+ field.defaultValue = info->dflt;
4340
+ field.type = info->type;
4341
+ field.numericMin = info->numeric_min;
4342
+ field.numericMax = info->numeric_max;
4343
+ field.numericStep = info->numeric_step;
4344
+
4345
+ for (int i = 0; i < info->combo_count; ++i) {
4346
+ field.options.emplace_back(info->combo_options[i]);
4347
+ }
4348
+
4349
+ schema_data->fields.push_back(field);
4350
+ return 1;
4351
+ }
4352
+
4353
+ Napi::Value NodeHamLib::GetConfigSchema(const Napi::CallbackInfo& info) {
4354
+ Napi::Env env = info.Env();
4355
+ RETURN_NULL_IF_RIG_HANDLE_INVALID();
4356
+
4357
+ RigConfigSchemaData schemaData{};
4358
+ int result = shim_rig_cfgparams_foreach(my_rig, rig_config_callback, &schemaData);
4359
+
4360
+ if (result != SHIM_RIG_OK) {
4361
+ Napi::Error::New(env, shim_rigerror(result)).ThrowAsJavaScriptException();
4362
+ return env.Null();
4363
+ }
4364
+
4365
+ Napi::Array schemaArray = Napi::Array::New(env, schemaData.fields.size());
4366
+ for (size_t i = 0; i < schemaData.fields.size(); ++i) {
4367
+ const RigConfigFieldDescriptor& descriptor = schemaData.fields[i];
4368
+ Napi::Object field = Napi::Object::New(env);
4369
+ field.Set("token", Napi::Number::New(env, descriptor.token));
4370
+ field.Set("name", Napi::String::New(env, descriptor.name));
4371
+ field.Set("label", Napi::String::New(env, descriptor.label));
4372
+ field.Set("tooltip", Napi::String::New(env, descriptor.tooltip));
4373
+ field.Set("defaultValue", Napi::String::New(env, descriptor.defaultValue));
4374
+ field.Set("type", Napi::String::New(env, publicConfTypeName(descriptor.type)));
4375
+
4376
+ if (descriptor.type == 2 || descriptor.type == 6) {
4377
+ Napi::Object numeric = Napi::Object::New(env);
4378
+ numeric.Set("min", Napi::Number::New(env, descriptor.numericMin));
4379
+ numeric.Set("max", Napi::Number::New(env, descriptor.numericMax));
4380
+ numeric.Set("step", Napi::Number::New(env, descriptor.numericStep));
4381
+ field.Set("numeric", numeric);
4382
+ }
4383
+
4384
+ if (!descriptor.options.empty()) {
4385
+ Napi::Array options = Napi::Array::New(env, descriptor.options.size());
4386
+ for (size_t optionIndex = 0; optionIndex < descriptor.options.size(); ++optionIndex) {
4387
+ options[static_cast<uint32_t>(optionIndex)] =
4388
+ Napi::String::New(env, descriptor.options[optionIndex]);
4389
+ }
4390
+ field.Set("options", options);
4391
+ }
4392
+
4393
+ schemaArray[static_cast<uint32_t>(i)] = field;
4394
+ }
4395
+
4396
+ return schemaArray;
4397
+ }
4398
+
4399
+ Napi::Value NodeHamLib::GetPortCaps(const Napi::CallbackInfo& info) {
4400
+ Napi::Env env = info.Env();
4401
+ RETURN_NULL_IF_RIG_HANDLE_INVALID();
4402
+
4403
+ shim_rig_port_caps_t caps{};
4404
+ int result = shim_rig_get_port_caps(my_rig, &caps);
4405
+
4406
+ if (result != SHIM_RIG_OK) {
4407
+ Napi::Error::New(env, shim_rigerror(result)).ThrowAsJavaScriptException();
4408
+ return env.Null();
4409
+ }
4410
+
4411
+ Napi::Object portCaps = Napi::Object::New(env);
4412
+ portCaps.Set("portType", Napi::String::New(env, caps.port_type));
4413
+ portCaps.Set("writeDelay", Napi::Number::New(env, caps.write_delay));
4414
+ portCaps.Set("postWriteDelay", Napi::Number::New(env, caps.post_write_delay));
4415
+ portCaps.Set("timeout", Napi::Number::New(env, caps.timeout));
4416
+ portCaps.Set("retry", Napi::Number::New(env, caps.retry));
4417
+
4418
+ if (hasPositiveValue(caps.serial_rate_min)) {
4419
+ portCaps.Set("serialRateMin", Napi::Number::New(env, caps.serial_rate_min));
4420
+ }
4421
+ if (hasPositiveValue(caps.serial_rate_max)) {
4422
+ portCaps.Set("serialRateMax", Napi::Number::New(env, caps.serial_rate_max));
4423
+ }
4424
+ if (hasPositiveValue(caps.serial_data_bits)) {
4425
+ portCaps.Set("serialDataBits", Napi::Number::New(env, caps.serial_data_bits));
4426
+ }
4427
+ if (hasPositiveValue(caps.serial_stop_bits)) {
4428
+ portCaps.Set("serialStopBits", Napi::Number::New(env, caps.serial_stop_bits));
4429
+ }
4430
+ if (caps.serial_parity[0] != '\0' && std::string(caps.serial_parity) != "Unknown") {
4431
+ portCaps.Set("serialParity", Napi::String::New(env, caps.serial_parity));
4432
+ }
4433
+ if (caps.serial_handshake[0] != '\0' && std::string(caps.serial_handshake) != "Unknown") {
4434
+ portCaps.Set("serialHandshake", Napi::String::New(env, caps.serial_handshake));
4435
+ }
4436
+
4437
+ return portCaps;
4438
+ }
4439
+
4241
4440
  // Power Control Methods
4242
4441
  Napi::Value NodeHamLib::SetPowerstat(const Napi::CallbackInfo& info) {
4243
4442
  Napi::Env env = info.Env();
@@ -6156,11 +6355,7 @@ private:
6156
6355
 
6157
6356
  Napi::Value NodeHamLib::SetConf(const Napi::CallbackInfo& info) {
6158
6357
  Napi::Env env = info.Env();
6159
-
6160
- if (!rig_is_open) {
6161
- Napi::TypeError::New(env, "Rig is not open!").ThrowAsJavaScriptException();
6162
- return env.Null();
6163
- }
6358
+ RETURN_NULL_IF_RIG_HANDLE_INVALID();
6164
6359
 
6165
6360
  if (info.Length() < 2 || !info[0].IsString() || !info[1].IsString()) {
6166
6361
  Napi::TypeError::New(env, "Expected (name: string, value: string)").ThrowAsJavaScriptException();
@@ -6212,11 +6407,7 @@ private:
6212
6407
 
6213
6408
  Napi::Value NodeHamLib::GetConf(const Napi::CallbackInfo& info) {
6214
6409
  Napi::Env env = info.Env();
6215
-
6216
- if (!rig_is_open) {
6217
- Napi::TypeError::New(env, "Rig is not open!").ThrowAsJavaScriptException();
6218
- return env.Null();
6219
- }
6410
+ RETURN_NULL_IF_RIG_HANDLE_INVALID();
6220
6411
 
6221
6412
  if (info.Length() < 1 || !info[0].IsString()) {
6222
6413
  Napi::TypeError::New(env, "Expected (name: string)").ThrowAsJavaScriptException();
package/src/hamlib.h CHANGED
@@ -99,6 +99,8 @@ class NodeHamLib : public Napi::ObjectWrap<NodeHamLib> {
99
99
  Napi::Value SetDcdType(const Napi::CallbackInfo&);
100
100
  Napi::Value GetDcdType(const Napi::CallbackInfo&);
101
101
  Napi::Value GetSupportedSerialConfigs(const Napi::CallbackInfo&);
102
+ Napi::Value GetConfigSchema(const Napi::CallbackInfo&);
103
+ Napi::Value GetPortCaps(const Napi::CallbackInfo&);
102
104
 
103
105
  // Power Control
104
106
  Napi::Value SetPowerstat(const Napi::CallbackInfo&);
@@ -242,6 +244,7 @@ class NodeHamLib : public Napi::ObjectWrap<NodeHamLib> {
242
244
 
243
245
  // Static callback helper for shim_rig_list_foreach
244
246
  static int rig_list_callback(const shim_rig_info_t* info, void* data);
247
+ static int rig_config_callback(const shim_confparam_info_t* info, void* data);
245
248
 
246
249
  void EmitSpectrumLine(const shim_spectrum_line_t& line);
247
250
  void StopSpectrumStreamInternal();