diff --git a/Makefile b/Makefile index a0bc777..c41b49d 100644 --- a/Makefile +++ b/Makefile @@ -29,8 +29,11 @@ build-server: $(MAKE) --no-print-directory -C $(BUILDDIR) simulith_server_standalone # Create components directory and copy shared libraries mkdir -p $(BUILDDIR)/components - cp $(BUILDDIR)/*.so $(BUILDDIR)/components/ 2>/dev/null || true - cp $(TOPDIR)/comp/demo/sim/build/*.so $(BUILDDIR)/components/ 2>/dev/null || true + @for dir in $(TOPDIR)/comp/*/sim/build ; do \ + if [ -d "$$dir" ] && [ -f "$$dir/Makefile" ]; then \ + cp $$dir/*.so $(BUILDDIR)/components/ 2>/dev/null || true; \ + fi; \ + done # Copy 42 configuration to build directory for runtime cp -r 42/InOut $(BUILDDIR)/ 2>/dev/null || true cp -r 42/Model $(BUILDDIR)/ 2>/dev/null || true diff --git a/include/simulith_i2c.h b/include/simulith_i2c.h index 4181f65..9ddd9b1 100644 --- a/include/simulith_i2c.h +++ b/include/simulith_i2c.h @@ -1,64 +1,84 @@ +/* + * Simulith I2C - Requirements + * + * Shall utilize ZMQ to communicate between nodes + * Shall have functions to initialize, read, write, transaction (both write then read), and close + * Shall communicate directly to the other end of the node + * Shall not block on any function + * Shall fail gracefully if peer is unavailable and return error codes (non-zero) instead of crashing. + * Shall not rely on a server as each node will be initialized with its name and destination. + */ + #ifndef SIMULITH_I2C_H #define SIMULITH_I2C_H -#include -#include +#include "simulith.h" + +#define SIMULITH_I2C_SUCCESS 0 +#define SIMULITH_I2C_ERROR -1 + +#define SIMULITH_I2C_INITIALIZED 255 + +#define SIMULITH_I2C_BASE_PORT 7000 #ifdef __cplusplus extern "C" { #endif - /** - * @brief Callback function type for I2C read operations - * @param addr Device address - * @param reg Register address - * @param data Buffer to store read data - * @param len Number of bytes to read - * @return 0 on success, -1 on failure - */ - typedef int (*simulith_i2c_read_callback)(uint8_t addr, uint8_t reg, uint8_t *data, size_t len); +typedef struct { + char name[64]; + char address[128]; + int is_server; + void* zmq_ctx; + void* zmq_sock; + int init; + uint8_t bus_id; + uint8_t device_addr; +} i2c_device_t; /** - * @brief Callback function type for I2C write operations - * @param addr Device address - * @param reg Register address - * @param data Data to write - * @param len Number of bytes to write - * @return 0 on success, -1 on failure + * @brief Initialize an I2C device + * @param device Pointer to I2C device structure + * @return SIMULITH_I2C_SUCCESS on success, SIMULITH_I2C_ERROR on failure */ - typedef int (*simulith_i2c_write_callback)(uint8_t addr, uint8_t reg, const uint8_t *data, size_t len); - - /** - * @brief Initialize an I2C bus - * @param bus_id Bus identifier (0-7) - * @param read_cb Callback function for read operations - * @param write_cb Callback function for write operations - * @return 0 on success, -1 on failure - */ - int simulith_i2c_init(uint8_t bus_id, simulith_i2c_read_callback read_cb, simulith_i2c_write_callback write_cb); + int simulith_i2c_init(i2c_device_t *device); /** * @brief Read data from an I2C device - * @param bus_id Bus identifier - * @param addr Device address - * @param reg Register address + * @param device Pointer to I2C device structure * @param data Buffer to store read data * @param len Number of bytes to read - * @return 0 on success, -1 on failure + * @return Number of bytes read on success, SIMULITH_I2C_ERROR on failure */ - int simulith_i2c_read(uint8_t bus_id, uint8_t addr, uint8_t reg, uint8_t *data, size_t len); + int simulith_i2c_read(i2c_device_t *device, uint8_t *data, size_t len); /** * @brief Write data to an I2C device - * @param bus_id Bus identifier - * @param addr Device address - * @param reg Register address + * @param device Pointer to I2C device structure * @param data Data to write * @param len Number of bytes to write - * @return 0 on success, -1 on failure + * @return Number of bytes written on success, SIMULITH_I2C_ERROR on failure + */ + int simulith_i2c_write(i2c_device_t *device, const uint8_t *data, size_t len); + + /** + * @brief Perform I2C transaction (write then read) + * @param device Pointer to I2C device structure + * @param tx_data Data to write + * @param tx_len Number of bytes to write + * @param rx_data Buffer to store read data + * @param rx_len Number of bytes to read + * @return SIMULITH_I2C_SUCCESS on success, SIMULITH_I2C_ERROR on failure + */ + int simulith_i2c_transaction(i2c_device_t *device, const uint8_t *tx_data, size_t tx_len, uint8_t *rx_data, size_t rx_len); + + /** + * @brief Close an I2C device + * @param device Pointer to I2C device structure + * @return SIMULITH_I2C_SUCCESS on success, SIMULITH_I2C_ERROR on failure */ - int simulith_i2c_write(uint8_t bus_id, uint8_t addr, uint8_t reg, const uint8_t *data, size_t len); + int simulith_i2c_close(i2c_device_t *device); #ifdef __cplusplus } diff --git a/include/simulith_uart.h b/include/simulith_uart.h index 4d01537..187916a 100644 --- a/include/simulith_uart.h +++ b/include/simulith_uart.h @@ -1,12 +1,12 @@ /* * Simulith UART - Requirements * - * - Shall utilize ZMQ to communicate between nodes - * - Shall have functions to initialize, send, receive, check available, and flush data - * - Shall communicate directly to the other end of the node - * - Shall not block on any function - * - Shall fail gracefully if peer is unavailable and return error codes (non-zero) instead of crashing. - * - Shall not rely on a server as each node will be initialized with its name and destination. + * Shall utilize ZMQ to communicate between nodes + * Shall have functions to initialize, send, receive, check available, and flush data + * Shall communicate directly to the other end of the node + * Shall not block on any function + * Shall fail gracefully if peer is unavailable and return error codes (non-zero) instead of crashing. + * Shall not rely on a server as each node will be initialized with its name and destination. */ #ifndef SIMULITH_UART_H @@ -26,7 +26,6 @@ extern "C" { #endif - typedef struct { char name[64]; char address[128]; diff --git a/src/simulith_i2c.c b/src/simulith_i2c.c index d62c5e5..2bde302 100644 --- a/src/simulith_i2c.c +++ b/src/simulith_i2c.c @@ -1,75 +1,126 @@ -#include "simulith.h" -#include +/* + * Simulith I2C - Requirements + * + * Shall utilize ZMQ to communicate between nodes + * Shall have functions to initialize, read, write, transaction (both write then read), and close + * Shall communicate directly to the other end of the node + * Shall not block on any function + * Shall fail gracefully if peer is unavailable and return error codes (non-zero) instead of crashing. + * Shall not rely on a server as each node will be initialized with its name and destination. + */ -#define MAX_I2C_BUSES 8 +#include "simulith_i2c.h" -typedef struct +int simulith_i2c_init(i2c_device_t *device) { - bool initialized; - simulith_i2c_read_callback read_cb; - simulith_i2c_write_callback write_cb; -} i2c_bus_t; + if (!device) return SIMULITH_I2C_ERROR; + if (device->init == SIMULITH_I2C_INITIALIZED) return SIMULITH_I2C_SUCCESS; -static i2c_bus_t i2c_buses[MAX_I2C_BUSES] = {0}; + device->zmq_ctx = zmq_ctx_new(); + if (!device->zmq_ctx) { + simulith_log("simulith_i2c_init: Failed to create ZMQ context\n"); + return SIMULITH_I2C_ERROR; + } + device->zmq_sock = zmq_socket(device->zmq_ctx, ZMQ_PAIR); + if (!device->zmq_sock) { + simulith_log("simulith_i2c_init: Failed to create ZMQ socket\n"); + zmq_ctx_term(device->zmq_ctx); + return SIMULITH_I2C_ERROR; + } + if (strlen(device->name) > 0) { + zmq_setsockopt(device->zmq_sock, ZMQ_IDENTITY, device->name, strlen(device->name)); + } + int rc; + if (device->is_server) { + rc = zmq_bind(device->zmq_sock, device->address); + if (rc != 0) { + simulith_log("simulith_i2c_init: Failed to bind to %s\n", device->address); + zmq_close(device->zmq_sock); + zmq_ctx_term(device->zmq_ctx); + return SIMULITH_I2C_ERROR; + } + simulith_log("simulith_i2c_init: Bound to %s as '%s'\n", device->address, device->name); + } else { + rc = zmq_connect(device->zmq_sock, device->address); + if (rc != 0) { + simulith_log("simulith_i2c_init: Failed to connect to %s\n", device->address); + zmq_close(device->zmq_sock); + zmq_ctx_term(device->zmq_ctx); + return SIMULITH_I2C_ERROR; + } + simulith_log("simulith_i2c_init: Connected to %s as '%s'\n", device->address, device->name); + } + device->init = SIMULITH_I2C_INITIALIZED; + return SIMULITH_I2C_SUCCESS; +} -int simulith_i2c_init(uint8_t bus_id, simulith_i2c_read_callback read_cb, simulith_i2c_write_callback write_cb) +int simulith_i2c_write(i2c_device_t *device, const uint8_t *data, size_t len) { - if (bus_id >= MAX_I2C_BUSES) - { - simulith_log("Invalid I2C bus ID: %d\n", bus_id); - errno = EINVAL; - return -1; + if (!device || device->init != SIMULITH_I2C_INITIALIZED) { + simulith_log("simulith_i2c_write: Uninitialized I2C device\n"); + return SIMULITH_I2C_ERROR; } - - if (!read_cb || !write_cb) - { - simulith_log("Invalid I2C callbacks\n"); - errno = EINVAL; - return -1; + int rc = zmq_send(device->zmq_sock, data, len, ZMQ_DONTWAIT); + if (rc < 0) { + simulith_log("simulith_i2c_write: zmq_send failed (peer may be unavailable)\n"); + return SIMULITH_I2C_ERROR; } - - i2c_buses[bus_id].initialized = true; - i2c_buses[bus_id].read_cb = read_cb; - i2c_buses[bus_id].write_cb = write_cb; - - simulith_log("I2C bus %d initialized\n", bus_id); - return 0; + simulith_log("I2C TX[%s]: %zu bytes\n", device->name, len); + return (int)len; } -int simulith_i2c_read(uint8_t bus_id, uint8_t addr, uint8_t reg, uint8_t *data, size_t len) +int simulith_i2c_read(i2c_device_t *device, uint8_t *data, size_t len) { - if (bus_id >= MAX_I2C_BUSES || !i2c_buses[bus_id].initialized) - { - simulith_log("Invalid or uninitialized I2C bus: %d\n", bus_id); - errno = EINVAL; - return -1; + if (!device || device->init != SIMULITH_I2C_INITIALIZED) { + simulith_log("simulith_i2c_read: Uninitialized I2C device\n"); + return SIMULITH_I2C_ERROR; } - - if (!data || len == 0) - { - simulith_log("Invalid read parameters\n"); - errno = EINVAL; - return -1; + int rc = zmq_recv(device->zmq_sock, data, len, ZMQ_DONTWAIT); + if (rc < 0) { + if (zmq_errno() == EAGAIN) { + // No data available, non-blocking + return 0; + } + simulith_log("simulith_i2c_read: zmq_recv failed (peer may be unavailable)\n"); + return SIMULITH_I2C_ERROR; } - - return i2c_buses[bus_id].read_cb(addr, reg, data, len); + simulith_log("I2C RX[%s]: %d bytes\n", device->name, rc); + return rc; } -int simulith_i2c_write(uint8_t bus_id, uint8_t addr, uint8_t reg, const uint8_t *data, size_t len) +int simulith_i2c_transaction(i2c_device_t *device, const uint8_t *tx_data, size_t tx_len, uint8_t *rx_data, size_t rx_len) { - if (bus_id >= MAX_I2C_BUSES || !i2c_buses[bus_id].initialized) - { - simulith_log("Invalid or uninitialized I2C bus: %d\n", bus_id); - errno = EINVAL; - return -1; + if (!device || device->init != SIMULITH_I2C_INITIALIZED) { + simulith_log("simulith_i2c_transaction: Uninitialized I2C device\n"); + return SIMULITH_I2C_ERROR; } - - if (!data || len == 0) - { - simulith_log("Invalid write parameters\n"); - errno = EINVAL; - return -1; + + // First, perform the write operation + int write_result = simulith_i2c_write(device, tx_data, tx_len); + if (write_result < 0) { + simulith_log("simulith_i2c_transaction: Write operation failed\n"); + return SIMULITH_I2C_ERROR; + } + + // Then, perform the read operation + int read_result = simulith_i2c_read(device, rx_data, rx_len); + if (read_result < 0) { + simulith_log("simulith_i2c_transaction: Read operation failed\n"); + return SIMULITH_I2C_ERROR; } + + simulith_log("I2C Transaction[%s]: wrote %d bytes, read %d bytes\n", device->name, write_result, read_result); + return SIMULITH_I2C_SUCCESS; +} - return i2c_buses[bus_id].write_cb(addr, reg, data, len); +int simulith_i2c_close(i2c_device_t *device) +{ + if (!device || device->init != SIMULITH_I2C_INITIALIZED) { + return SIMULITH_I2C_ERROR; + } + zmq_close(device->zmq_sock); + zmq_ctx_term(device->zmq_ctx); + device->init = 0; + simulith_log("I2C device %s closed\n", device->name); + return SIMULITH_I2C_SUCCESS; } \ No newline at end of file diff --git a/src/simulith_uart.c b/src/simulith_uart.c index 77fab82..18df0ab 100644 --- a/src/simulith_uart.c +++ b/src/simulith_uart.c @@ -1,14 +1,13 @@ /* - * Simulith UART + * Simulith UART - Requirements * - * Requirements: - * - Shall utilize ZMQ to communicate between nodes - * - Shall have functions to initialize, send, receive, check available, and flush data - * - Shall communicate directly to the other end of the node - * - Shall not block on any function - * - Shall fail gracefully if peer is unavailable and return error codes (non-zero) instead of crashing - * - Shall not rely on a server as each node will be initialized with its name and destination -*/ + * Shall utilize ZMQ to communicate between nodes + * Shall have functions to initialize, send, receive, check available, and flush data + * Shall communicate directly to the other end of the node + * Shall not block on any function + * Shall fail gracefully if peer is unavailable and return error codes (non-zero) instead of crashing. + * Shall not rely on a server as each node will be initialized with its name and destination. + */ #include "simulith_uart.h" diff --git a/test/test_i2c.c b/test/test_i2c.c index 2fc2f7b..5555576 100644 --- a/test/test_i2c.c +++ b/test/test_i2c.c @@ -11,80 +11,68 @@ void tearDown(void) // Cleanup code if needed } -static int test_i2c_read_cb(uint8_t addr, uint8_t reg, uint8_t *data, size_t len) +void test_i2c_device_init(void) { - if (addr == 0x50 && reg == 0x00 && len == 2) - { - data[0] = 0xAA; - data[1] = 0xBB; - return 0; + i2c_device_t device = {0}; + + // Set up device parameters + snprintf(device.name, sizeof(device.name), "TestI2C"); + snprintf(device.address, sizeof(device.address), "tcp://127.0.0.1:7050"); + device.is_server = 0; + device.bus_id = 0; + device.device_addr = 0x50; + + // Test NULL device + TEST_ASSERT_EQUAL_INT(SIMULITH_I2C_ERROR, simulith_i2c_init(NULL)); + + // Test device initialization (will fail without actual ZMQ endpoint, but tests the flow) + int result = simulith_i2c_init(&device); + // Since we don't have a peer, this will likely fail with connection error, which is expected + // The important thing is that it doesn't crash and returns proper error code + TEST_ASSERT_TRUE(result == SIMULITH_I2C_SUCCESS || result == SIMULITH_I2C_ERROR); + + // Clean up if it was successful + if (result == SIMULITH_I2C_SUCCESS) { + simulith_i2c_close(&device); } - return -1; } -static int test_i2c_write_cb(uint8_t addr, uint8_t reg, const uint8_t *data, size_t len) +void test_i2c_device_write_read(void) { - if (addr == 0x50 && reg == 0x00 && len == 2) - { - return (data[0] == 0xCC && data[1] == 0xDD) ? 0 : -1; - } - return -1; -} - -void test_i2c_init(void) -{ - // Test invalid bus ID - TEST_ASSERT_EQUAL_INT(-1, simulith_i2c_init(8, test_i2c_read_cb, test_i2c_write_cb)); - - // Test NULL callbacks - TEST_ASSERT_EQUAL_INT(-1, simulith_i2c_init(0, NULL, test_i2c_write_cb)); - TEST_ASSERT_EQUAL_INT(-1, simulith_i2c_init(0, test_i2c_read_cb, NULL)); - - // Test successful initialization - TEST_ASSERT_EQUAL_INT(0, simulith_i2c_init(0, test_i2c_read_cb, test_i2c_write_cb)); -} - -void test_i2c_read_write(void) -{ - // Initialize I2C bus first - TEST_ASSERT_EQUAL_INT(0, simulith_i2c_init(0, test_i2c_read_cb, test_i2c_write_cb)); - - // Test read operation - uint8_t read_data[2]; - TEST_ASSERT_EQUAL_INT(0, simulith_i2c_read(0, 0x50, 0x00, read_data, 2)); - TEST_ASSERT_EQUAL_HEX8(0xAA, read_data[0]); - TEST_ASSERT_EQUAL_HEX8(0xBB, read_data[1]); - - // Test write operation - uint8_t write_data[] = {0xCC, 0xDD}; - TEST_ASSERT_EQUAL_INT(0, simulith_i2c_write(0, 0x50, 0x00, write_data, 2)); + i2c_device_t device = {0}; + + // Test operations on uninitialized device + uint8_t test_data[] = {0xAA, 0xBB}; + uint8_t read_buffer[10]; + + TEST_ASSERT_EQUAL_INT(SIMULITH_I2C_ERROR, simulith_i2c_write(&device, test_data, 2)); + TEST_ASSERT_EQUAL_INT(SIMULITH_I2C_ERROR, simulith_i2c_read(&device, read_buffer, 2)); + TEST_ASSERT_EQUAL_INT(SIMULITH_I2C_ERROR, simulith_i2c_transaction(&device, test_data, 2, read_buffer, 2)); + + // Test NULL device + TEST_ASSERT_EQUAL_INT(SIMULITH_I2C_ERROR, simulith_i2c_write(NULL, test_data, 2)); + TEST_ASSERT_EQUAL_INT(SIMULITH_I2C_ERROR, simulith_i2c_read(NULL, read_buffer, 2)); + TEST_ASSERT_EQUAL_INT(SIMULITH_I2C_ERROR, simulith_i2c_transaction(NULL, test_data, 2, read_buffer, 2)); } -void test_i2c_invalid_params(void) +void test_i2c_device_close(void) { - // Initialize I2C bus first - TEST_ASSERT_EQUAL_INT(0, simulith_i2c_init(0, test_i2c_read_cb, test_i2c_write_cb)); - - uint8_t data[2]; - - // Test invalid read parameters - TEST_ASSERT_EQUAL_INT(-1, simulith_i2c_read(8, 0x50, 0x00, data, 2)); // Invalid bus - TEST_ASSERT_EQUAL_INT(-1, simulith_i2c_read(0, 0x50, 0x00, NULL, 2)); // NULL buffer - TEST_ASSERT_EQUAL_INT(-1, simulith_i2c_read(0, 0x50, 0x00, data, 0)); // Zero length - - // Test invalid write parameters - TEST_ASSERT_EQUAL_INT(-1, simulith_i2c_write(8, 0x50, 0x00, data, 2)); // Invalid bus - TEST_ASSERT_EQUAL_INT(-1, simulith_i2c_write(0, 0x50, 0x00, NULL, 2)); // NULL buffer - TEST_ASSERT_EQUAL_INT(-1, simulith_i2c_write(0, 0x50, 0x00, data, 0)); // Zero length + i2c_device_t device = {0}; + + // Test close on uninitialized device + TEST_ASSERT_EQUAL_INT(SIMULITH_I2C_ERROR, simulith_i2c_close(&device)); + + // Test close on NULL device + TEST_ASSERT_EQUAL_INT(SIMULITH_I2C_ERROR, simulith_i2c_close(NULL)); } int main(void) { UNITY_BEGIN(); - RUN_TEST(test_i2c_init); - RUN_TEST(test_i2c_read_write); - RUN_TEST(test_i2c_invalid_params); + RUN_TEST(test_i2c_device_init); + RUN_TEST(test_i2c_device_write_read); + RUN_TEST(test_i2c_device_close); return UNITY_END(); } \ No newline at end of file