You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by cd...@apache.org on 2020/04/28 16:43:21 UTC
[plc4x] branch feature/c-api-read-support-no-promises updated: -
Started implementing the core SPI logic - Implemented a connection string
parser including testsuite - Implemented the functionality for searching
for a matching driver
This is an automated email from the ASF dual-hosted git repository.
cdutz pushed a commit to branch feature/c-api-read-support-no-promises
in repository https://gitbox.apache.org/repos/asf/plc4x.git
The following commit(s) were added to refs/heads/feature/c-api-read-support-no-promises by this push:
new d4dc98d - Started implementing the core SPI logic - Implemented a connection string parser including testsuite - Implemented the functionality for searching for a matching driver
d4dc98d is described below
commit d4dc98d8311b452d931a157e5f8bd84ad32e3e02
Author: Christofer Dutz <ch...@c-ware.de>
AuthorDate: Tue Apr 28 18:43:12 2020 +0200
- Started implementing the core SPI logic
- Implemented a connection string parser including testsuite
- Implemented the functionality for searching for a matching driver
---
sandbox/plc4c/api/include/plc4c/system.h | 102 ++++----
sandbox/plc4c/api/include/plc4c/types.h | 7 +-
.../plc4c/drivers/simulated/src/driver_simulated.c | 1 +
.../plc4c/examples/hello-world/src/hello_world.c | 19 +-
sandbox/plc4c/pom.xml | 3 +-
sandbox/plc4c/spi/CMakeLists.txt | 12 +-
.../plc4c/spi/include/plc4c/spi/system_private.h | 28 +++
.../plc4c/spi/include/plc4c/spi/types_private.h | 60 +++--
sandbox/plc4c/spi/src/connection.c | 2 +-
sandbox/plc4c/spi/src/system.c | 262 +++++++++++++++++++--
sandbox/plc4c/spi/test/system_test.c | 78 ++++++
11 files changed, 484 insertions(+), 90 deletions(-)
diff --git a/sandbox/plc4c/api/include/plc4c/system.h b/sandbox/plc4c/api/include/plc4c/system.h
index 4e9799e..24bec8a 100644
--- a/sandbox/plc4c/api/include/plc4c/system.h
+++ b/sandbox/plc4c/api/include/plc4c/system.h
@@ -31,57 +31,65 @@ extern "C" {
/**
* Function pointer for a callback called when a driver is loaded.
- * Set in plc4c_system @see plc4c_system_set_on_driver_loaded()
+ * Set in plc4c_system @see plc4c_system_set_on_driver_load_success_callback()
+ *
* @param driver
*/
-typedef void (*plc4c_system_callback_on_driver_loaded)(plc4c_driver *driver);
+typedef void (*plc4c_system_on_driver_load_success_callback)(plc4c_driver *driver);
/**
* Function pointer for a callback called when loading a driver fails.
- * Set in plc4c_system @see plc4c_system_set_on_driver_load_error
+ * Set in plc4c_system @see plc4c_system_set_on_driver_load_failure_callback
* NOTE: driver_name could be a pointer to the configuration for the driver instead....
+ *
* @param driver_name
* @param error_code
*/
-typedef void (*plc4c_system_callback_driver_load_error)(const char *driver_name, return_code error);
+typedef void (*plc4c_system_on_driver_load_failure_callback)(const char *driver_name, return_code error);
/**
* Function pointer for a callback called when is successfully made
- * Set in plc4c_system @see plc4c_system_set_on_connection()
+ * Set in plc4c_system @see plc4c_system_set_on_connect_success_callback().
+ *
* @param connection
*/
-typedef void (*plc4c_system_callback_on_connection)(plc4c_connection *connection);
+typedef void (*plc4c_system_on_connect_success_callback)(plc4c_connection *connection);
/**
* Function pointer for a callback called when connecting fails.
- * Set in plc4c_system @see plc4c_system_set_on_connection_error
+ * Set in plc4c_system @see plc4c_system_set_on_connect_failure_callback.
+ *
* @param connection_string
* @param error_code
*/
-typedef void (*plc4c_system_callback_connection_error)(const char *connection_string, return_code error);
+typedef void (*plc4c_system_on_connect_failure_callback)(const char *connection_string, return_code error);
/**
* Function pointer for a callback called when is successfully made
- * Set in plc4c_system @see plc4c_system_set_on_connection()
+ * Set in plc4c_system @see plc4c_system_set_on_disconnect_success_callback().
+ *
* @param connection
*/
-typedef void (*plc4c_system_callback_on_disconnection)(plc4c_connection *connection);
+typedef void (*plc4c_system_on_disconnect_success_callback)(plc4c_connection *connection);
/**
* Function pointer for a callback called when connecting fails.
- * Set in plc4c_system @see plc4c_system_set_on_connection_error
+ * Set in plc4c_system @see plc4c_system_set_on_disconnect_failure_callback.
+ *
* @param connection
* @param error_code
*/
-typedef void (*plc4c_system_callback_disconnection_error)(plc4c_connection *connection, return_code error);
+typedef void (*plc4c_system_on_disconnect_failure_callback)(plc4c_connection *connection, return_code error);
/**
- * Function pointer for a callback called when a driver returns an error
+ * Function pointer for a callback called when a driver returns an error.
+ * Set in plc4c_system @see plc4c_system_set_on_loop_failure_callback.
+ *
* @param driver
* @param connection
* @param error_code
*/
-typedef void(*plc4c_system_callback_loop_error)
+typedef void(*plc4c_system_on_loop_failure_callback)
(plc4c_driver *driver, plc4c_connection *connection, return_code error);
@@ -95,84 +103,97 @@ typedef void(*plc4c_system_callback_loop_error)
*/
/**
- * Function to create a plc4c_system
+ * Function to create a plc4c_system.
+ *
* @param system
* @return NO_MEMORY if failed to create system
*/
return_code plc4c_system_create(plc4c_system **system);
/**
- * Function to destroy a plc4c_system
- * This will also destroy all connections associated with the system
+ * Function to destroy a plc4c_system.
+ * This will also destroy all connections associated with the system.
+ *
* @param system
*/
void plc4c_system_destroy(plc4c_system *system);
/**
- * Function to set the on_driver_loaded callback for the plc4c system
+ * Function to set the on_driver_loaded callback for the plc4c system.
+ *
* @param system
* @param callback plc4c_system_callback_on_driver
*/
-void plc4c_system_set_on_driver_loaded(plc4c_system *system,
- plc4c_system_callback_on_driver_loaded callback);
+void plc4c_system_set_on_driver_load_success_callback(plc4c_system *system,
+ plc4c_system_on_driver_load_success_callback callback);
/**
- * Function to set the error callback for loading drivers for the plc4c system
+ * Function to set the error callback for loading drivers for the plc4c system.
+ *
* @param system
* @param callback plc4c_system_callback_driver_load_error
*/
-void plc4c_system_set_on_driver_load_error(plc4c_system *system,
- plc4c_system_callback_driver_load_error callback);
+void plc4c_system_set_on_driver_load_failure_callback(plc4c_system *system,
+ plc4c_system_on_driver_load_failure_callback callback);
/**
- * Function to set the on_connection callback for the plc4c system
+ * Function to set the on_connection callback for the plc4c system.
+ *
* @param system
* @param callback plc4c_system_callback_on_connection
*/
-void plc4c_system_set_on_connection(plc4c_system *system,
- plc4c_system_callback_on_connection callback);
+void plc4c_system_set_on_connect_success_callback(plc4c_system *system,
+ plc4c_system_on_connect_success_callback callback);
/**
- * Function to set the error callback for making connections for the plc4c system
+ * Function to set the error callback for making connections for the plc4c system.
+ *
* @param system
- * @param callback plc4c_system_callback_connection_error
+ * @param callback plc4c_system_on_connect_failure_callback
*/
-void plc4c_system_set_on_connection_error(plc4c_system *system,
- plc4c_system_callback_connection_error callback);
+void plc4c_system_set_on_connect_failure_callback(plc4c_system *system,
+ plc4c_system_on_connect_failure_callback callback);
/**
- * Function to set the on_disconnection callback for the plc4c system
+ * Function to set the on_disconnection callback for the plc4c system.
+ *
* @param system
* @param callback plc4c_system_callback_on_disconnection
*/
-void plc4c_system_set_on_disconnection(plc4c_system *system,
- plc4c_system_callback_on_disconnection callback);
+void plc4c_system_set_on_disconnect_success_callback(plc4c_system *system,
+ plc4c_system_on_disconnect_success_callback callback);
/**
- * Function to set the error callback for shutting down connections for the plc4c system
+ * Function to set the error callback for shutting down connections for the plc4c system.
+ *
* @param system
* @param callback
*/
-void plc4c_system_set_on_disconnection_error(plc4c_system *system,
- plc4c_system_callback_disconnection_error callback);
+void plc4c_system_set_on_disconnect_failure_callback(plc4c_system *system,
+ plc4c_system_on_disconnect_failure_callback callback);
/**
- * Function to set the error callback loops
+ * Function to set the error callback loops.
+ *
* @param system
* @param callback plc4c_system_callback_loop_error
*/
-void plc4c_system_set_on_loop_error(plc4c_system *system,
- plc4c_system_callback_loop_error callback);
+void plc4c_system_set_on_loop_failure_callback(plc4c_system *system,
+ plc4c_system_on_loop_failure_callback callback);
/**
* Function to manually add a driver to the system.
+ *
+ * @param system the system the driver should be added to.
* @param driver instance of the driver
* @return return_code
*/
-return_code plc4c_system_add_driver(plc4c_driver *driver);
+return_code plc4c_system_add_driver(plc4c_system *system,
+ plc4c_driver *driver);
/**
* Function to initialize the PLC4C system (Initialize the driver manager and the list of enabled drivers)
+ *
* @param system
* @return return_code
*/
@@ -180,6 +201,7 @@ return_code plc4c_system_init(plc4c_system *system);
/**
* Function to clean up the PLC4C system (Free any still used resources, terminate live connections, ...)
+ *
* @param system
*/
void plc4c_system_shutdown(plc4c_system *system);
diff --git a/sandbox/plc4c/api/include/plc4c/types.h b/sandbox/plc4c/api/include/plc4c/types.h
index 2ef112b..eea69e5 100644
--- a/sandbox/plc4c/api/include/plc4c/types.h
+++ b/sandbox/plc4c/api/include/plc4c/types.h
@@ -31,11 +31,16 @@ extern "C" {
typedef enum return_code {
UNFINISHED,
OK,
- UNKNOWN_ERROR,
NO_MEMORY,
INVALID_CONNECTION_STRING,
NOT_REACHABLE,
PERMISSION_DENIED,
+
+ NO_DRIVER_AVAILABLE,
+ UNKNOWN_DRIVER,
+ UNSPECIFIED_TRANSPORT,
+
+ UNKNOWN_ERROR,
INTERNAL_ERROR
} return_code;
diff --git a/sandbox/plc4c/drivers/simulated/src/driver_simulated.c b/sandbox/plc4c/drivers/simulated/src/driver_simulated.c
index 11aab46..5af83fb 100644
--- a/sandbox/plc4c/drivers/simulated/src/driver_simulated.c
+++ b/sandbox/plc4c/drivers/simulated/src/driver_simulated.c
@@ -38,6 +38,7 @@ plc4c_driver *plc4c_driver_simulated_create() {
plc4c_driver* driver = (plc4c_driver*) malloc(sizeof(plc4c_driver));
driver->protocol_code = "simulated";
driver->protocol_name = "Simulated PLC4X Datasource";
+ driver->default_transport_code = "dummy";
driver->parse_address_function = &plc4c_driver_simulated_parse_address;
return driver;
}
diff --git a/sandbox/plc4c/examples/hello-world/src/hello_world.c b/sandbox/plc4c/examples/hello-world/src/hello_world.c
index f5aad2e..c372011 100644
--- a/sandbox/plc4c/examples/hello-world/src/hello_world.c
+++ b/sandbox/plc4c/examples/hello-world/src/hello_world.c
@@ -40,7 +40,7 @@ void onGlobalDisconnect(plc4c_connection *cur_connection) {
}
enum plc4c_connection_state_t {
- PRE_CONNECTION,
+ CONNECTING,
CONNECTED,
READ_REQUEST_SENT,
READ_RESPONSE_RECEIVED,
@@ -64,7 +64,7 @@ int main() {
// Manually register the "simulated" driver with the system.
plc4c_driver *simulatedDriver = plc4c_driver_simulated_create();
- result = plc4c_system_add_driver(simulatedDriver);
+ result = plc4c_system_add_driver(system, simulatedDriver);
if (result != OK) {
return -1;
}
@@ -76,24 +76,28 @@ int main() {
}
// Register the global callbacks.
- plc4c_system_set_on_connection(system, &onGlobalConnect);
- plc4c_system_set_on_disconnection(system, &onGlobalDisconnect);
+ plc4c_system_set_on_connect_success_callback(system, &onGlobalConnect);
+ plc4c_system_set_on_disconnect_success_callback(system, &onGlobalDisconnect);
// Establish connections to remote devices
// you may or may not care about the connection handle
- result = plc4c_system_connect(system, "s7://192.168.42.20", &connection);
+ result = plc4c_system_connect(system, "simulated://foo", &connection);
if (result != OK) {
return -1;
}
// Central program loop ...
- plc4c_connection_state state = PRE_CONNECTION;
+ plc4c_connection_state state = CONNECTING;
while (loop) {
+ // Give plc4c a chance to do something.
+ // This is where all I/O is being done.
if (plc4c_system_loop(system) != OK) {
break;
}
+
+ // Depending on the current state, implement some logic.
switch (state) {
- case PRE_CONNECTION: {
+ case CONNECTING: {
// Check if the connection is established:
if (plc4c_connection_is_connected(connection)) {
state = CONNECTED;
@@ -157,6 +161,7 @@ int main() {
case DISCONNECTED: {
// End the loop.
loop = false;
+ break;
}
}
}
diff --git a/sandbox/plc4c/pom.xml b/sandbox/plc4c/pom.xml
index 193166b..4af6c7b 100644
--- a/sandbox/plc4c/pom.xml
+++ b/sandbox/plc4c/pom.xml
@@ -281,7 +281,8 @@
</configuration>
</execution>
</executions>
- </plugin> </plugins>
+ </plugin>
+ </plugins>
</build>
<!-- This dependency is just to ensure thrift is built first -->
diff --git a/sandbox/plc4c/spi/CMakeLists.txt b/sandbox/plc4c/spi/CMakeLists.txt
index d006973..542a924 100644
--- a/sandbox/plc4c/spi/CMakeLists.txt
+++ b/sandbox/plc4c/spi/CMakeLists.txt
@@ -21,4 +21,14 @@ include_directories("include" "../api/include")
file(GLOB sources "src/*.c")
-add_library(plc4c-spi SHARED ${sources})
\ No newline at end of file
+add_library(plc4c-spi SHARED ${sources})
+
+if(BUILD_PHASE STREQUAL test-compile)
+ file(GLOB testSources "test/*.c")
+ add_executable(plc4c-spi-test ${testSources} test/system_test.c)
+ target_link_libraries (plc4c-spi-test
+ plc4c-spi
+ unity
+ )
+ add_test(NAME plc4c-spi-test COMMAND plc4c-spi-test)
+endif()
\ No newline at end of file
diff --git a/sandbox/plc4c/spi/include/plc4c/spi/system_private.h b/sandbox/plc4c/spi/include/plc4c/spi/system_private.h
new file mode 100644
index 0000000..71e675e
--- /dev/null
+++ b/sandbox/plc4c/spi/include/plc4c/spi/system_private.h
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#ifndef PLC4C_SPI_SYSTEM_PRIVATE_H_
+#define PLC4C_SPI_SYSTEM_PRIVATE_H_
+
+#include <plc4c/types.h>
+#include <plc4c/system.h>
+
+return_code plc4c_system_create_connection(const char *connection_string,
+ plc4c_connection **connection);
+
+#endif //PLC4C_SPI_SYSTEM_PRIVATE_H_
diff --git a/sandbox/plc4c/spi/include/plc4c/spi/types_private.h b/sandbox/plc4c/spi/include/plc4c/spi/types_private.h
index d353c53..8bb4cb6 100644
--- a/sandbox/plc4c/spi/include/plc4c/spi/types_private.h
+++ b/sandbox/plc4c/spi/include/plc4c/spi/types_private.h
@@ -20,51 +20,81 @@
#define PLC4C_SPI_TYPES_PRIVATE_H_
#include <plc4c/types.h>
+#include <plc4c/system.h>
+
+typedef struct plc4c_item_t plc4c_item;
+typedef struct plc4c_driver_list_item_t plc4c_driver_list_item;
+typedef struct plc4c_connection_list_item_t plc4c_connection_list_item;
+typedef struct plc4c_write_item_t plc4c_write_item;
+
+typedef plc4c_item *(*plc4c_connection_parse_address_item)(const char *address_string);
struct plc4c_system_t {
- /* drivers */
+ /* drivers */
+ plc4c_driver_list_item *driver_list_head;
- /* connections */
+ /* connections */
+ plc4c_connection_list_item *connection_list_head;
- /* callbacks */
+ /* callbacks */
+ plc4c_system_on_driver_load_success_callback on_driver_load_success_callback;
+ plc4c_system_on_driver_load_failure_callback on_driver_load_failure_callback;
+ plc4c_system_on_connect_success_callback on_connect_success_callback;
+ plc4c_system_on_connect_failure_callback on_connect_failure_callback;
+ plc4c_system_on_disconnect_success_callback on_disconnect_success_callback;
+ plc4c_system_on_disconnect_failure_callback on_disconnect_failure_callback;
+ plc4c_system_on_loop_failure_callback on_loop_failure_callback;
};
struct plc4c_item_t {
};
-typedef struct plc4c_item_t plc4c_item;
-
-typedef plc4c_item* (*plc4c_connection_parse_address_item)(const char *address_string);
struct plc4c_driver_t {
- char* protocol_code;
- char* protocol_name;
+ char *protocol_code;
+ char *protocol_name;
+ char *default_transport_code;
plc4c_connection_parse_address_item parse_address_function;
};
+struct plc4c_driver_list_item_t {
+ plc4c_driver *driver;
+ plc4c_driver_list_item *next;
+};
+
+
struct plc4c_connection_t {
- plc4c_driver driver;
- char* connection_string;
+ const char *connection_string;
+ char *protocol_code;
+ char *transport_code;
+ char *transport_connect_information;
+ char *parameters;
+
+ plc4c_driver* driver;
bool supports_reading;
bool supports_writing;
bool supports_subscriptions;
};
+struct plc4c_connection_list_item_t {
+ plc4c_connection connection;
+ plc4c_connection_list_item *next;
+};
+
struct plc4c_read_request_t {
- plc4c_connection* connection;
+ plc4c_connection *connection;
int num_items;
plc4c_item items[];
};
struct plc4c_write_item_t {
plc4c_item *item;
- void* value;
+ void *value;
};
-typedef struct plc4c_write_item_t plc4c_write_item;
struct plc4c_write_request_t {
- plc4c_connection* connection;
+ plc4c_connection *connection;
int num_items;
- plc4c_write_item* items;
+ plc4c_write_item *items;
};
#endif //PLC4C_SPI_TYPES_PRIVATE_H_
diff --git a/sandbox/plc4c/spi/src/connection.c b/sandbox/plc4c/spi/src/connection.c
index debf9f8..9ab61a3 100644
--- a/sandbox/plc4c/spi/src/connection.c
+++ b/sandbox/plc4c/spi/src/connection.c
@@ -59,7 +59,7 @@ return_code plc4c_connection_create_write_request(plc4c_connection *connection,
new_write_request->items = malloc(num_items * sizeof(plc4c_write_item*));
for(int i = 0; i < num_items; i++) {
char* address = addresses[i];
- plc4c_item* addressItem = connection->driver.parse_address_function(address);
+ plc4c_item* addressItem = connection->driver->parse_address_function(address);
plc4c_write_item* write_item = malloc(sizeof(plc4c_write_item));
write_item->item = addressItem;
diff --git a/sandbox/plc4c/spi/src/system.c b/sandbox/plc4c/spi/src/system.c
index aa8761d..bdf8b66 100644
--- a/sandbox/plc4c/spi/src/system.c
+++ b/sandbox/plc4c/spi/src/system.c
@@ -17,58 +17,96 @@
* under the License.
*/
+#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include <plc4c/system.h>
#include "plc4c/spi/types_private.h"
+#include "plc4c/spi/system_private.h"
return_code plc4c_system_create(plc4c_system **system) {
+ plc4c_system *new_system = malloc(sizeof(plc4c_system));
+ new_system->driver_list_head = NULL;
+ new_system->connection_list_head = NULL;
+ *system = new_system;
return OK;
}
void plc4c_system_destroy(plc4c_system *system) {
-
+ // TODO: So some more cleaning up ...
+ free(system);
}
-void plc4c_system_set_on_driver_loaded(plc4c_system *system,
- plc4c_system_callback_on_driver_loaded callback) {
-
+void plc4c_system_set_on_driver_load_success_callback(plc4c_system *system,
+ plc4c_system_on_driver_load_success_callback callback) {
+ system->on_driver_load_success_callback = callback;
}
-void plc4c_system_set_on_driver_load_error(plc4c_system *system,
- plc4c_system_callback_driver_load_error callback) {
-
+void plc4c_system_set_on_driver_load_failure_callback(plc4c_system *system,
+ plc4c_system_on_driver_load_failure_callback callback) {
+ system->on_driver_load_failure_callback = callback;
}
-void plc4c_system_set_on_connection(plc4c_system *system,
- plc4c_system_callback_on_connection callback) {
-
+void plc4c_system_set_on_connect_success_callback(plc4c_system *system,
+ plc4c_system_on_connect_success_callback callback) {
+ system->on_connect_success_callback = callback;
}
-void plc4c_system_set_on_connection_error(plc4c_system *system,
- plc4c_system_callback_connection_error callback) {
-
+void plc4c_system_set_on_connect_failure_callback(plc4c_system *system,
+ plc4c_system_on_connect_failure_callback callback) {
+ system->on_connect_failure_callback = callback;
}
-void plc4c_system_set_on_disconnection(plc4c_system *system,
- plc4c_system_callback_on_disconnection callback) {
-
+void plc4c_system_set_on_disconnect_success_callback(plc4c_system *system,
+ plc4c_system_on_disconnect_success_callback callback) {
+ system->on_disconnect_success_callback = callback;
}
-void plc4c_system_set_on_disconnection_error(plc4c_system *system,
- plc4c_system_callback_disconnection_error callback) {
+void plc4c_system_set_on_disconnection_failure_callback(plc4c_system *system,
+ plc4c_system_on_disconnect_failure_callback callback) {
+ system->on_disconnect_failure_callback = callback;
+}
+void plc4c_system_set_on_loop_failure_callback(plc4c_system *system,
+ plc4c_system_on_loop_failure_callback callback) {
+ system->on_loop_failure_callback = callback;
}
-void plc4c_system_set_on_loop_error(plc4c_system *system,
- plc4c_system_callback_loop_error callback) {
+return_code plc4c_system_add_driver(plc4c_system *system,
+ plc4c_driver *driver) {
+ // If the system is not initialized, return an error.
+ // There is nothing we can do here.
+ if (system == NULL) {
+ return INTERNAL_ERROR;
+ }
-}
+ // Get the first element of the driver list.
+ plc4c_driver_list_item *cur_driver = system->driver_list_head;
-return_code plc4c_system_add_driver(plc4c_driver *driver) {
+ // If the driver list is empty. Start a new list of drivers.
+ if (cur_driver == NULL) {
+ system->driver_list_head = malloc(sizeof(plc4c_driver_list_item));
+ system->driver_list_head->driver = driver;
+ system->driver_list_head->next = NULL;
+ }
+ // Drivers are already listed, add the currentdriver to the end of the list.
+ else {
+ // Go to the last driver in the list.
+ while (cur_driver->next != NULL) {
+ cur_driver = cur_driver->next;
+ }
+
+ // Add a new driver item to the end of the list.
+ cur_driver->next = malloc(sizeof(plc4c_driver_list_item));
+ cur_driver->next->driver = driver;
+ cur_driver->next->next = NULL;
+ }
return OK;
}
return_code plc4c_system_init(plc4c_system *system) {
+ // Nothing to really do at the moment.
+
return OK;
}
@@ -76,9 +114,185 @@ void plc4c_system_shutdown(plc4c_system *system) {
}
+enum connection_string_parser_state {
+ PROTOCOL_CODE,
+ TRANSPORT_CODE,
+ TRANSPORT_CONNECTION_INFORMATION,
+ PARAMETERS,
+ FINISHED
+};
+
+return_code plc4c_system_create_connection(const char *connection_string, plc4c_connection** connection) {
+ // Count the number of colons and question-marks so we know which pattern to use for
+ // matching and how large the arrays for containing the different segments should be.
+ int num_colons = 0;
+ int num_question_marks = 0;
+ char* protocol_code = NULL;
+ char* transport_code = NULL;
+ char* transport_connect_information = NULL;
+ char* parameters = NULL;
+ int start_segment_index = 0;
+ const char* start_segment = connection_string;
+ // The connection string has two parts ... the first, where colons are the delimiters
+ // and the second where a question mark is the delimiter.
+ enum mode {
+ SEARCHING_FOR_COLONS,
+ SEARCHING_FOR_QUESTION_MARKS
+ } mode = SEARCHING_FOR_COLONS;
+ for(int i = 0; i <= strlen(connection_string); i++) {
+ // If we're in the first part of the connection string ... watch out for colons.
+ if(mode == SEARCHING_FOR_COLONS) {
+ // If we encounter a colon, depending on the number of colons already found, save the information in
+ // either the protocol code or transport code variable.
+ if (*(connection_string + i) == ':') {
+ num_colons++;
+ // The first colon delimits the protocol-code.
+ if (num_colons == 1) {
+ // Allocate enough memory to hold the sub-string.
+ protocol_code = malloc(sizeof(char) * ((i - start_segment_index) + 1));
+ // Copy the sub-string to the freshly allocated memory area.
+ strlcpy(protocol_code, start_segment, (i - start_segment_index) + 1);
+
+ // Set the start of the next segment to directly after the colon.
+ start_segment_index = i + 1;
+ start_segment = connection_string + start_segment_index;
+
+ // If the following character would be a slash, we're probably finished and no transport code is
+ // provided. If this is the case, ensure it's actually a double-slash and if this is the case
+ // switch to the question-mark searching mode.
+ if(*start_segment == '/') {
+ if(*(start_segment + 1) == '/') {
+ mode = SEARCHING_FOR_QUESTION_MARKS;
+ start_segment_index += 2;
+ start_segment += 2;
+ } else {
+ return INVALID_CONNECTION_STRING;
+ }
+ }
+ }
+ // If we encountered a second colon, this is the transport code.
+ else if (num_colons == 2) {
+ // Allocate enough memory to hold the sub-string.
+ transport_code = malloc(sizeof(char) * ((i - start_segment_index) + 1));
+ // Copy the sub-string to the freshly allocated memory area.
+ strlcpy(transport_code, start_segment, (i - start_segment_index) + 1);
+
+ // Set the start of the next segment to directly after the colon.
+ start_segment_index = i + 1;
+ start_segment = connection_string + start_segment_index;
+
+ // The transport code is allways followed by "://". So check if this is the case.
+ // If it is, switch to question-mark searching mode.
+ if((*start_segment != '/') || (*(start_segment + 1) != '/')) {
+ return INVALID_CONNECTION_STRING;
+ }
+ mode = SEARCHING_FOR_QUESTION_MARKS;
+
+ // Bump the start of the segment to after the double slashes.
+ start_segment_index += 2;
+ start_segment += 2;
+ } else {
+ return INVALID_CONNECTION_STRING;
+ }
+ }
+ }
+ // If we're in the second part, look for question marks.
+ else {
+ // The question-mark separates the transport connect information from the parameters.
+ if (*(connection_string + i) == '?') {
+ num_question_marks++;
+ // Only one question-mark is allowed in connection strings.
+ if (num_question_marks > 1) {
+ return INVALID_CONNECTION_STRING;
+ }
+
+ // Allocate enough memory to hold the sub-string.
+ transport_connect_information = malloc(sizeof(char) * ((i - start_segment_index) + 1));
+ // Copy the sub-string to the freshly allocated memory area.
+ strlcpy(transport_connect_information, start_segment, (i - start_segment_index) + 1);
+
+ // Set the start of the next segment to directly after the question-mark.
+ start_segment_index = i + 1;
+ start_segment = connection_string + start_segment_index;
+ }
+ // This is the last character ... finish up the last loose end.
+ if (i == strlen(connection_string)) {
+ // If no question-mark has been encountered, this connection string doesn't have one and the
+ // remaining part is simply the transport connect information.
+ if (num_question_marks == 0) {
+ transport_connect_information = malloc(sizeof(char) * ((i - start_segment_index) + 1));
+ strlcpy(transport_connect_information, start_segment, (i - start_segment_index) + 1);
+ }
+ // I a question-mark was found, this is the paramters section.
+ else {
+ parameters = malloc(sizeof(char) * (i - start_segment_index));
+ strlcpy(parameters, start_segment, (i - start_segment_index) + 1);
+ }
+ }
+ }
+ }
+ if(num_colons == 0) {
+ return INVALID_CONNECTION_STRING;
+ }
+
+ // Initialize a new connection data-structure with the parsed information.
+ plc4c_connection* new_connection = malloc(sizeof(plc4c_connection));
+ new_connection->connection_string = connection_string;
+ new_connection->protocol_code = protocol_code;
+ new_connection->transport_code = transport_code;
+ new_connection->transport_connect_information = transport_connect_information;
+ new_connection->parameters = parameters;
+ *connection = new_connection;
+
+ return OK;
+}
+
return_code plc4c_system_connect(plc4c_system *system,
- const char *connectionString,
- plc4c_connection **connection) {
+ const char *connection_string,
+ plc4c_connection **connection) {
+
+ // Parse the connection string and initialize some of the connection field variables from this.
+ plc4c_connection* new_connection = NULL;
+ return_code result = plc4c_system_create_connection(connection_string, &new_connection);
+ if(result != OK) {
+ return result;
+ }
+
+ // Find a matching driver from the driver-list
+ plc4c_driver_list_item* cur_driver_list_item = system->driver_list_head;
+ // If no driver is available at all this is devinitely a developer error,
+ // so we output a special error code for this case
+ if(cur_driver_list_item == NULL) {
+ return NO_DRIVER_AVAILABLE;
+ }
+ do {
+ if (strcmp(cur_driver_list_item->driver->protocol_code, new_connection->protocol_code) == 0) {
+ // Set the driver reference in the new connection.
+ new_connection->driver = cur_driver_list_item->driver;
+ // If no transport was selected, use the drivers default transport (if it exists).
+ if(new_connection->transport_code == NULL) {
+ if(cur_driver_list_item->driver->default_transport_code != NULL) {
+ new_connection->transport_code = cur_driver_list_item->driver->default_transport_code;
+ }
+ }
+ break;
+ }
+ cur_driver_list_item = cur_driver_list_item->next;
+ } while (cur_driver_list_item != NULL);
+
+ // If the driver property is still NULL, the desired driver was not found.
+ if(new_connection->driver == NULL) {
+ return UNKNOWN_DRIVER;
+ }
+
+ // Return an error if the user didn't specify a transport and the driver doesn't have a default one.
+ if(new_connection->transport_code == NULL) {
+ return UNSPECIFIED_TRANSPORT;
+ }
+
+ // Pass the new connection back.
+ *connection = new_connection;
+
return OK;
}
diff --git a/sandbox/plc4c/spi/test/system_test.c b/sandbox/plc4c/spi/test/system_test.c
new file mode 100644
index 0000000..223eb83
--- /dev/null
+++ b/sandbox/plc4c/spi/test/system_test.c
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#include <unity.h>
+#include <stdlib.h>
+#include <plc4c/spi/types_private.h>
+#include "plc4c/spi/system_private.h"
+
+void setUp(void) {
+}
+
+void tearDown(void) {
+}
+
+void test_system_plc4c_system_create_connection_args(const char *connection_string,
+ return_code expected_return_code,
+ const char *expected_connection_string,
+ const char *expected_protocol_code,
+ const char *expected_transport_code,
+ const char *expected_transport_connect_information,
+ const char *expected_parameters) {
+ plc4c_connection *connection = NULL;
+ return_code result = plc4c_system_create_connection(connection_string, &connection);
+ TEST_ASSERT_EQUAL(expected_return_code, result);
+ if(expected_return_code != OK) {
+ TEST_ASSERT_NULL(connection);
+ } else {
+ TEST_ASSERT_EQUAL_STRING(expected_connection_string, connection->connection_string);
+ TEST_ASSERT_EQUAL_STRING(expected_protocol_code, connection->protocol_code);
+ TEST_ASSERT_EQUAL_STRING(expected_transport_code, connection->transport_code);
+ TEST_ASSERT_EQUAL_STRING(expected_transport_connect_information, connection->transport_connect_information);
+ TEST_ASSERT_EQUAL_STRING(expected_parameters, connection->parameters);
+ free(connection);
+ }
+}
+
+void test_system_plc4c_system_create_connection(void) {
+ test_system_plc4c_system_create_connection_args("s7://1.2.3.4", OK, "s7://1.2.3.4", "s7", NULL, "1.2.3.4", NULL);
+ test_system_plc4c_system_create_connection_args("s7:tcp://1.2.3.4", OK, "s7:tcp://1.2.3.4", "s7", "tcp", "1.2.3.4", NULL);
+ test_system_plc4c_system_create_connection_args("s7://1.2.3.4?params", OK, "s7://1.2.3.4?params", "s7", NULL, "1.2.3.4", "params");
+ test_system_plc4c_system_create_connection_args("s7:tcp://1.2.3.4?params", OK, "s7:tcp://1.2.3.4?params", "s7", "tcp", "1.2.3.4", "params");
+
+ // A colon after the "://" shouldn't matter ...
+ test_system_plc4c_system_create_connection_args("s7://1.2.3.4:42", OK, "s7://1.2.3.4:42", "s7", NULL, "1.2.3.4:42", NULL);
+ test_system_plc4c_system_create_connection_args("s7://1.2.3.4?param=a:42", OK, "s7://1.2.3.4?param=a:42", "s7", NULL, "1.2.3.4", "param=a:42");
+
+ // Well obviously the parser shouldn't be able to find anything here ...
+ test_system_plc4c_system_create_connection_args("hurz", INVALID_CONNECTION_STRING, NULL, NULL, NULL, NULL, NULL);
+ // In these cases the parser expects a "//" after the second colon, which isn't there ...
+ test_system_plc4c_system_create_connection_args("a:b:c://d", INVALID_CONNECTION_STRING, NULL, NULL, NULL, NULL, NULL);
+ test_system_plc4c_system_create_connection_args("a:b:d", INVALID_CONNECTION_STRING, NULL, NULL, NULL, NULL, NULL);
+
+ // There should only be one question-mark ...
+ test_system_plc4c_system_create_connection_args("a://a?b?c", INVALID_CONNECTION_STRING, NULL, NULL, NULL, NULL, NULL);
+}
+
+int main(void) {
+ UNITY_BEGIN();
+
+ RUN_TEST(test_system_plc4c_system_create_connection);
+
+ return UNITY_END();
+}
\ No newline at end of file