#include #include #include #include "lua_bindings.h" #include "lua_boxed_numerics.h" #include #include #include extern const AP_HAL::HAL& hal; // millis int lua_millis(lua_State *L) { binding_argcheck(L, 0); new_uint32_t(L); *check_uint32_t(L, -1) = AP_HAL::millis(); return 1; } // micros int lua_micros(lua_State *L) { binding_argcheck(L, 0); new_uint32_t(L); *check_uint32_t(L, -1) = AP_HAL::micros(); return 1; } int lua_mission_receive(lua_State *L) { binding_argcheck(L, 0); ObjectBuffer *input = AP::scripting()->mission_data; if (input == nullptr) { // no mission items ever received return 0; } struct AP_Scripting::scripting_mission_cmd cmd; if (!input->pop(cmd)) { // no new item return 0; } new_uint32_t(L); *check_uint32_t(L, -1) = cmd.time_ms; lua_pushinteger(L, cmd.p1); lua_pushnumber(L, cmd.content_p1); lua_pushnumber(L, cmd.content_p2); lua_pushnumber(L, cmd.content_p3); return 5; } int AP_Logger_Write(lua_State *L) { AP_Logger * AP_logger = AP_Logger::get_singleton(); if (AP_logger == nullptr) { return luaL_argerror(L, 1, "logger not supported on this firmware"); } // Allow : and . access const int arg_offset = (luaL_testudata(L, 1, "logger") != NULL) ? 1 : 0; // check we have at least 4 arguments passed in const int args = lua_gettop(L) - arg_offset; if (args < 4) { return luaL_argerror(L, args, "too few arguments"); } const char * name = luaL_checkstring(L, 1 + arg_offset); const char * labels = luaL_checkstring(L, 2 + arg_offset); const char * fmt = luaL_checkstring(L, 3 + arg_offset); // cheack the name, labels and format are not too long if (strlen(name) >= LS_NAME_SIZE) { return luaL_error(L, "Name must be 4 or less chars long"); } uint8_t length = strlen(labels); if (length >= (LS_LABELS_SIZE - 7)) { // need 7 chars to add 'TimeUS,' return luaL_error(L, "labels must be less than 58 chars long"); } // Count the number of commas uint8_t commas = 1; for (uint8_t i=0; i= (LS_FORMAT_SIZE - 1)) { // need 1 char to add timestamp return luaL_error(L, "format must be less than 15 chars long"); } // check the number of arguments matches the number of values in the label if (length != commas) { return luaL_argerror(L, args, "label does not match format"); } bool have_units = false; if (args - 5 == length) { // check if there are enough arguments for units and multiplyers have_units = true; } else if (args - 3 != length) { // check the number of arguments matches the length of the foramt string return luaL_argerror(L, args, "format does not match No. of arguments"); } // prepend timestamp to format and labels char label_cat[LS_LABELS_SIZE]; strcpy(label_cat,"TimeUS,"); strcat(label_cat,labels); char fmt_cat[LS_FORMAT_SIZE]; strcpy(fmt_cat,"Q"); strcat(fmt_cat,fmt); // Need to declare these here so they don't go out of scope char units_cat[LS_FORMAT_SIZE]; char multipliers_cat[LS_FORMAT_SIZE]; uint8_t field_start = 4; struct AP_Logger::log_write_fmt *f; if (!have_units) { // ask for a mesage type f = AP_logger->msg_fmt_for_name(name, label_cat, nullptr, nullptr, fmt_cat, true); } else { // read in units and multiplers strings field_start += 2; const char * units = luaL_checkstring(L, 4 + arg_offset); const char * multipliers = luaL_checkstring(L, 5 + arg_offset); if (length != strlen(units)) { return luaL_error(L, "units must be same length as format"); } if (length != strlen(multipliers)) { return luaL_error(L, "multipliers must be same length as format"); } // prepend timestamp to units and multiplyers strcpy(units_cat,"s"); strcat(units_cat,units); strcpy(multipliers_cat,"F"); strcat(multipliers_cat,multipliers); // ask for a mesage type f = AP_logger->msg_fmt_for_name(name, label_cat, units_cat, multipliers_cat, fmt_cat, true); } if (f == nullptr) { // unable to map name to a messagetype; could be out of // msgtypes, could be out of slots, ... return luaL_argerror(L, args, "could not map message type"); } // work out how long the block will be int16_t msg_len = AP_logger->Write_calc_msg_len(fmt_cat); if (msg_len == -1) { return luaL_argerror(L, args, "unknown format"); } luaL_Buffer buffer; luaL_buffinit(L, &buffer); // add logging headers const char header[2] = {(char)HEAD_BYTE1, (char)HEAD_BYTE2}; luaL_addlstring(&buffer, header, sizeof(header)); luaL_addlstring(&buffer, (char *)&f->msg_type, sizeof(f->msg_type)); // timestamp is always first value const uint64_t now = AP_HAL::micros64(); luaL_addlstring(&buffer, (char *)&now, sizeof(uint64_t)); for (uint8_t i=field_start; i<=args; i++) { uint8_t charlen = 0; uint8_t index = have_units ? i-5 : i-3; uint8_t arg_index = i + arg_offset; switch(fmt_cat[index]) { // logger varable types not available to scripting // 'b': int8_t // 'h': int16_t // 'c': int16_t // 'd': double // 'H': uint16_t // 'C': uint16_t // 'Q': uint64_t // 'q': int64_t // 'a': arrays case 'i': case 'L': case 'e': { const lua_Integer tmp1 = luaL_checkinteger(L, arg_index); luaL_argcheck(L, ((tmp1 >= INT32_MIN) && (tmp1 <= INT32_MAX)), arg_index, "argument out of range"); int32_t tmp = tmp1; luaL_addlstring(&buffer, (char *)&tmp, sizeof(int32_t)); break; } case 'f': { float tmp = luaL_checknumber(L, arg_index); luaL_argcheck(L, ((tmp >= -INFINITY) && (tmp <= INFINITY)), arg_index, "argument out of range"); luaL_addlstring(&buffer, (char *)&tmp, sizeof(float)); break; } case 'n': { charlen = 4; break; } case 'M': case 'B': { const lua_Integer tmp1 = luaL_checkinteger(L, arg_index); luaL_argcheck(L, ((tmp1 >= 0) && (tmp1 <= UINT8_MAX)), arg_index, "argument out of range"); uint8_t tmp = static_cast(tmp1); luaL_addlstring(&buffer, (char *)&tmp, sizeof(uint8_t)); break; } case 'I': case 'E': { const uint32_t tmp = coerce_to_uint32_t(L, arg_index); luaL_addlstring(&buffer, (char *)&tmp, sizeof(uint32_t)); break; } case 'N': { charlen = 16; break; } case 'Z': { charlen = 64; break; } default: { return luaL_error(L, "%c unsupported format",fmt_cat[arg_index-3]); } } if (charlen != 0) { const char *tmp = luaL_checkstring(L, arg_index); const size_t slen = strlen(tmp); if (slen > charlen) { return luaL_error(L, "arg %i too long for %c format",arg_index,fmt_cat[arg_index-3]); } char tstr[charlen]; memcpy(tstr, tmp, slen); if (slen < charlen) { memset(&tstr[slen], 0, charlen-slen); } luaL_addlstring(&buffer, tstr, charlen); } } AP_logger->Safe_Write_Emit_FMT(f); luaL_pushresult(&buffer); AP_logger->WriteBlock(buffer.b,msg_len); return 0; } int lua_get_i2c_device(lua_State *L) { // Allow : and . access const int arg_offset = (luaL_testudata(L, 1, "i2c") != NULL) ? 1 : 0; const int args = lua_gettop(L) - arg_offset; if (args < 2) { return luaL_argerror(L, args, "require i2c bus and address"); } if (args > 4) { return luaL_argerror(L, args, "too many arguments"); } const lua_Integer bus_in = luaL_checkinteger(L, 1 + arg_offset); luaL_argcheck(L, ((bus_in >= 0) && (bus_in <= 4)), 1 + arg_offset, "bus out of range"); const uint8_t bus = static_cast(bus_in); const lua_Integer address_in = luaL_checkinteger(L, 2 + arg_offset); luaL_argcheck(L, ((address_in >= 0) && (address_in <= 128)), 2 + arg_offset, "address out of range"); const uint8_t address = static_cast(address_in); // optional arguments, use the same defaults as the hal get_device function uint32_t bus_clock = 400000; bool use_smbus = false; if (args > 2) { bus_clock = coerce_to_uint32_t(L, 3 + arg_offset); if (args > 3) { use_smbus = static_cast(lua_toboolean(L, 4 + arg_offset)); } } static_assert(SCRIPTING_MAX_NUM_I2C_DEVICE >= 0, "There cannot be a negative number of I2C devices"); if (AP::scripting()->num_i2c_devices >= SCRIPTING_MAX_NUM_I2C_DEVICE) { return luaL_argerror(L, 1, "no i2c devices available"); } AP::scripting()->_i2c_dev[AP::scripting()->num_i2c_devices] = new AP_HAL::OwnPtr; if (AP::scripting()->_i2c_dev[AP::scripting()->num_i2c_devices] == nullptr) { return luaL_argerror(L, 1, "i2c device nullptr"); } *AP::scripting()->_i2c_dev[AP::scripting()->num_i2c_devices] = std::move(hal.i2c_mgr->get_device(bus, address, bus_clock, use_smbus)); if (AP::scripting()->_i2c_dev[AP::scripting()->num_i2c_devices] == nullptr || AP::scripting()->_i2c_dev[AP::scripting()->num_i2c_devices]->get() == nullptr) { return luaL_argerror(L, 1, "i2c device nullptr"); } new_AP_HAL__I2CDevice(L); *check_AP_HAL__I2CDevice(L, -1) = AP::scripting()->_i2c_dev[AP::scripting()->num_i2c_devices]->get(); AP::scripting()->num_i2c_devices++; return 1; } int AP_HAL__I2CDevice_read_registers(lua_State *L) { const int args = lua_gettop(L); bool multi_register; if (args == 2) { multi_register = false; } else if (args == 3) { multi_register = true; } else { return luaL_argerror(L, args, "expected 1 or 2 arguments"); } AP_HAL::I2CDevice * ud = *check_AP_HAL__I2CDevice(L, 1); if (ud == NULL) { return luaL_error(L, "Internal error, null pointer"); } const lua_Integer raw_first_reg = luaL_checkinteger(L, 2); luaL_argcheck(L, ((raw_first_reg >= MAX(0, 0)) && (raw_first_reg <= MIN(UINT8_MAX, UINT8_MAX))), 2, "argument out of range"); const uint8_t first_reg = static_cast(raw_first_reg); uint8_t recv_length = 1; if (multi_register) { const lua_Integer raw_recv_length = luaL_checkinteger(L, 3); luaL_argcheck(L, ((raw_recv_length >= MAX(0, 0)) && (raw_recv_length <= MIN(UINT8_MAX, UINT8_MAX))), 3, "argument out of range"); recv_length = static_cast(raw_recv_length); } uint8_t data[recv_length]; ud->get_semaphore()->take_blocking(); const bool success = static_cast(ud->read_registers(first_reg, data, recv_length)); ud->get_semaphore()->give(); if (success) { if (!multi_register) { lua_pushinteger(L, data[0]); } else { // push to table lua_newtable(L); for (uint8_t i=0; i < recv_length; i++) { lua_pushinteger(L, i+1); lua_pushinteger(L, data[i]); lua_settable(L, -3); } } } return success; } #if HAL_MAX_CAN_PROTOCOL_DRIVERS int lua_get_CAN_device(lua_State *L) { // Allow : and . access const int arg_offset = (luaL_testudata(L, 1, "CAN") != NULL) ? 1 : 0; binding_argcheck(L, 1 + arg_offset); const uint32_t raw_buffer_len = coerce_to_uint32_t(L, 1 + arg_offset); luaL_argcheck(L, ((raw_buffer_len >= 1U) && (raw_buffer_len <= 25U)), 1 + arg_offset, "argument out of range"); const uint32_t buffer_len = static_cast(raw_buffer_len); if (AP::scripting()->_CAN_dev == nullptr) { AP::scripting()->_CAN_dev = new ScriptingCANSensor(AP_CANManager::Driver_Type::Driver_Type_Scripting); if (AP::scripting()->_CAN_dev == nullptr) { return luaL_argerror(L, 1, "CAN device nullptr"); } } new_ScriptingCANBuffer(L); *check_ScriptingCANBuffer(L, -1) = AP::scripting()->_CAN_dev->add_buffer(buffer_len); return 1; } int lua_get_CAN_device2(lua_State *L) { // Allow : and . access const int arg_offset = (luaL_testudata(L, 1, "CAN") != NULL) ? 1 : 0; binding_argcheck(L, 1 + arg_offset); const uint32_t raw_buffer_len = coerce_to_uint32_t(L, 1 + arg_offset); luaL_argcheck(L, ((raw_buffer_len >= 1U) && (raw_buffer_len <= 25U)), 1 + arg_offset, "argument out of range"); const uint32_t buffer_len = static_cast(raw_buffer_len); if (AP::scripting()->_CAN_dev2 == nullptr) { AP::scripting()->_CAN_dev2 = new ScriptingCANSensor(AP_CANManager::Driver_Type::Driver_Type_Scripting2); if (AP::scripting()->_CAN_dev2 == nullptr) { return luaL_argerror(L, 1, "CAN device nullptr"); } } new_ScriptingCANBuffer(L); *check_ScriptingCANBuffer(L, -1) = AP::scripting()->_CAN_dev2->add_buffer(buffer_len); return 1; } #endif // HAL_MAX_CAN_PROTOCOL_DRIVERS