blob: 4e5590c1af8f1ddda3585abb4ce5a4efa30ffdab [file] [log] [blame]
/*
* 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
*
* https://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 <plc4c/driver_simulated.h>
#include <plc4c/plc4c.h>
#include <plc4c/spi/types_private.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
enum plc4c_driver_simulated_tag_type_t { RANDOM, STATE, STDOUT };
typedef enum plc4c_driver_simulated_tag_type_t
plc4c_driver_simulated_tag_type;
typedef enum plc4c_driver_simulated_tag_datatype_t
plc4c_driver_simulated_tag_datatype;
// State definitions
enum plc4c_driver_simulated_disconnect_states {
PLC4C_DRIVER_SIMULATED_DISCONNECT_INIT,
PLC4C_DRIVER_SIMULATED_DISCONNECT_WAIT_TASKS_FINISHED,
PLC4C_DRIVER_SIMULATED_DISCONNECT_FINISHED
};
enum read_states {
PLC4C_DRIVER_SIMULATED_READ_INIT,
PLC4C_DRIVER_SIMULATED_READ_FINISHED
};
enum write_states {
PLC4C_DRIVER_SIMULATED_WRITE_INIT,
PLC4C_DRIVER_SIMULATED_WRITE_FINISHED
};
struct plc4c_driver_simulated_item_t {
char *name;
plc4c_driver_simulated_tag_type type;
plc4c_data_type data_type;
int num_elements;
};
typedef struct plc4c_driver_simulated_item_t plc4c_driver_simulated_item;
plc4c_return_code plc4c_driver_simulated_connect_machine_function(
plc4c_system_task *task) {
plc4c_connection *connection = task->context;
if (connection == NULL) {
return INTERNAL_ERROR;
}
if (plc4c_connection_get_connected(connection)) {
return ALREADY_CONNECTED;
}
plc4c_connection_set_connected(connection, true);
task->completed = true;
return OK;
}
plc4c_return_code plc4c_driver_simulated_disconnect_machine_function(
plc4c_system_task *task) {
plc4c_connection *connection = task->context;
if (connection == NULL) {
return INTERNAL_ERROR;
}
switch (task->state_id) {
case PLC4C_DRIVER_SIMULATED_DISCONNECT_INIT: {
plc4c_connection_set_disconnect(connection, true);
task->state_id = PLC4C_DRIVER_SIMULATED_DISCONNECT_WAIT_TASKS_FINISHED;
break;
}
case PLC4C_DRIVER_SIMULATED_DISCONNECT_WAIT_TASKS_FINISHED: {
// The disconnect system-task also counts.
if (plc4c_connection_get_running_tasks_count(connection) == 1) {
plc4c_connection_set_connected(connection, false);
task->completed = true;
task->state_id = PLC4C_DRIVER_SIMULATED_DISCONNECT_FINISHED;
}
break;
}
case PLC4C_DRIVER_SIMULATED_DISCONNECT_FINISHED: {
// Do nothing
break;
}
default: {
return INTERNAL_ERROR;
}
}
return OK;
}
plc4c_return_code plc4c_driver_simulated_read_machine_function(
plc4c_system_task *task) {
if (task->context == NULL) {
return INTERNAL_ERROR;
}
plc4c_read_request_execution *read_request_execution = task->context;
plc4c_read_request *read_request = read_request_execution->read_request;
switch (task->state_id) {
case PLC4C_DRIVER_SIMULATED_READ_INIT: {
// Create a response.
plc4c_read_response *read_response = malloc(sizeof(plc4c_read_response));
read_response->read_request = read_request;
plc4c_utils_list_create(&(read_response->items));
// Process every tag in the request.
plc4c_list_element *cur_element =
plc4c_utils_list_head(read_request->items);
while (cur_element != NULL) {
plc4c_driver_simulated_item *cur_item = cur_element->value;
plc4c_response_value_item *value_item =
malloc(sizeof(plc4c_response_value_item));
value_item->item = (plc4c_item *)cur_item;
value_item->response_code = PLC4C_RESPONSE_CODE_OK;
/*
* create the plc4c_data
* if this were a custom type we could set a custom destroy method
* we can also set a custom printf method
* right , now just create a new random value
*/
value_item->value = plc4c_data_create_dint_data(rand());
// Add the value to the response.
plc4c_utils_list_insert_tail_value(read_response->items, value_item);
cur_element = cur_element->next;
}
read_request_execution->read_response = read_response;
task->state_id = PLC4C_DRIVER_SIMULATED_READ_FINISHED;
task->completed = true;
break;
}
case PLC4C_DRIVER_SIMULATED_READ_FINISHED: {
// Do nothing
break;
}
default: {
return INTERNAL_ERROR;
}
}
return OK;
}
plc4c_return_code plc4c_driver_simulated_write_machine_function(
plc4c_system_task *task) {
if (task->context == NULL) {
return INTERNAL_ERROR;
}
plc4c_write_request_execution *write_request_execution = task->context;
plc4c_write_request *write_request = write_request_execution->write_request;
switch (task->state_id) {
case PLC4C_DRIVER_SIMULATED_WRITE_INIT: {
// Create a response.
plc4c_write_response *write_response =
malloc(sizeof(plc4c_write_response));
write_response->write_request = write_request;
plc4c_utils_list_create(&(write_response->response_items));
// Process every tag in the request.
plc4c_list_element *cur_element =
plc4c_utils_list_head(write_request->items);
while (cur_element != NULL) {
plc4c_request_value_item *cur_value_item = cur_element->value;
plc4c_driver_simulated_item *cur_item =
(plc4c_driver_simulated_item *)cur_value_item->item;
plc4c_data *write_data = cur_value_item->value;
plc4c_response_code response_code = -1;
switch (cur_item->type) {
case STDOUT: {
printf("");
if (cur_item->data_type != write_data->data_type) {
printf(
"--> Simulated Driver Write: Value is %s but Item type is %s",
plc4c_data_type_name(write_data->data_type),
plc4c_data_type_name(cur_item->data_type));
response_code = PLC4C_RESPONSE_CODE_INVALID_DATATYPE;
break;
}
printf("--> Simulated Driver Write: Value (%s) %s: ",
plc4c_data_type_name(write_data->data_type), cur_item->name);
plc4c_data_printf(write_data);
printf("\n");
response_code = PLC4C_RESPONSE_CODE_OK;
break;
}
default: {
response_code = PLC4C_RESPONSE_CODE_INVALID_ADDRESS;
break;
}
}
// Create a response element and add that to the response ...
plc4c_response_item *response_item =
malloc(sizeof(plc4c_response_item));
response_item->item = (plc4c_item *)cur_item;
response_item->response_code = response_code;
plc4c_utils_list_insert_tail_value(write_response->response_items,
response_item);
cur_element = cur_element->next;
}
write_request_execution->write_response = write_response;
task->state_id = PLC4C_DRIVER_SIMULATED_WRITE_FINISHED;
task->completed = true;
break;
}
case PLC4C_DRIVER_SIMULATED_WRITE_FINISHED: {
// Do nothing
break;
}
default: {
return INTERNAL_ERROR;
}
}
return OK;
}
plc4c_return_code plc4c_driver_simulated_encode_address(char *address_string, void** item) {
plc4c_driver_simulated_tag_type type = RANDOM;
char *name = NULL;
plc4c_data_type data_type = -1;
int num_elements = 0;
int start_segment_index = 0;
char *start_segment = address_string;
for (int i = 0; i <= strlen(address_string); i++) {
// This marks the end of the type part.
if (*(address_string + i) == '/') {
char *type_str = malloc(sizeof(char) * (i + 1));
strncpy(type_str, start_segment, i);
if (strcmp(type_str, "RANDOM") == 0) {
type = RANDOM;
} else if (strcmp(type_str, "STATE") == 0) {
type = STATE;
} else if (strcmp(type_str, "STDOUT") == 0) {
type = STDOUT;
} else {
free(type_str);
return INVALID_ADDRESS;
}
free(type_str);
start_segment = address_string + i + 1;
start_segment_index = i + 1;
}
// This marks the end of the name part.
if (*(address_string + i) == ':') {
name = malloc(sizeof(char) * ((i - start_segment_index) + 1));
strncpy(name, start_segment, (i - start_segment_index));
start_segment = address_string + i + 1;
start_segment_index = i + 1;
}
// This marks the end of the data-type part if there is a size coming in
// addition.
if ((i == strlen(address_string)) || (*(address_string + i) == '[')) {
char *datatype_name =
malloc(sizeof(char) * ((i - start_segment_index) + 1));
strncpy(datatype_name, start_segment, (i - start_segment_index));
// Translate the string into a constant.
if (strcmp(datatype_name, "INTEGER") == 0) {
data_type = PLC4C_INT;
} else if (strcmp(datatype_name, "STRING") == 0) {
data_type = PLC4C_STRING;
} else {
free(datatype_name);
free(name);
return INVALID_ADDRESS;
}
free(datatype_name);
start_segment = address_string + i + 1;
start_segment_index = i + 1;
if (i == strlen(address_string)) {
num_elements = 1;
break;
}
}
// This marks the end of the size part.
if (*(address_string + i) == ']') {
char *num_elements_str =
malloc(sizeof(char) * ((i - start_segment_index) + 1));
strncpy(num_elements_str, start_segment, (i - start_segment_index));
char *success = NULL;
num_elements = (int)strtol(num_elements_str, &success, 10);
free(num_elements_str);
break;
}
}
// Create a new driver specific item.
plc4c_driver_simulated_item *curItem = (plc4c_driver_simulated_item *)malloc(
sizeof(plc4c_driver_simulated_item));
curItem->type = type;
curItem->name = name;
curItem->data_type = data_type;
curItem->num_elements = num_elements;
*item = curItem;
return OK;
}
plc4c_return_code plc4c_driver_simulated_connect_function(
plc4c_connection *connection, plc4c_system_task **task) {
plc4c_system_task *new_task = malloc(sizeof(plc4c_system_task));
// There's nothing to do here, so no need for a state-machine.
new_task->state_id = -1;
new_task->state_machine_function =
&plc4c_driver_simulated_connect_machine_function;
new_task->completed = false;
new_task->context = connection;
new_task->connection = connection;
*task = new_task;
return OK;
}
plc4c_return_code plc4c_driver_simulated_disconnect_function(
plc4c_connection *connection, plc4c_system_task **task) {
plc4c_system_task *new_task = malloc(sizeof(plc4c_system_task));
new_task->state_id = PLC4C_DRIVER_SIMULATED_DISCONNECT_INIT;
new_task->state_machine_function =
&plc4c_driver_simulated_disconnect_machine_function;
new_task->completed = false;
new_task->context = connection;
new_task->connection = connection;
*task = new_task;
return OK;
}
plc4c_return_code plc4c_driver_simulated_read_function(
plc4c_read_request_execution *read_request_execution,
plc4c_system_task **task) {
plc4c_system_task *new_task = malloc(sizeof(plc4c_system_task));
new_task->state_id = PLC4C_DRIVER_SIMULATED_READ_INIT;
new_task->state_machine_function =
&plc4c_driver_simulated_read_machine_function;
new_task->completed = false;
new_task->context = read_request_execution;
new_task->connection = read_request_execution->read_request->connection;
read_request_execution->system_task = new_task;
*task = new_task;
return OK;
}
plc4c_return_code plc4c_driver_simulated_write_function(
plc4c_write_request_execution *write_request_execution,
plc4c_system_task **task) {
plc4c_system_task *new_task = malloc(sizeof(plc4c_system_task));
new_task->state_id = PLC4C_DRIVER_SIMULATED_WRITE_INIT;
new_task->state_machine_function =
&plc4c_driver_simulated_write_machine_function;
new_task->completed = false;
new_task->context = write_request_execution;
new_task->connection = write_request_execution->write_request->connection;
write_request_execution->system_task = new_task;
*task = new_task;
return OK;
}
void plc4c_driver_simulated_free_read_response_item(
plc4c_list_element *read_item_element) {
plc4c_response_value_item *value_item =
(plc4c_response_value_item *)read_item_element->value;
plc4c_data_destroy(value_item->value);
value_item->value = NULL;
}
void plc4c_driver_simulated_free_read_response(plc4c_read_response *response) {
// the request will be cleaned up elsewhere
plc4c_utils_list_delete_elements(response->items,
&plc4c_driver_simulated_free_read_response_item);
}
void plc4c_driver_simulated_free_write_response_item(
plc4c_list_element *write_item_element) {
plc4c_response_value_item *value_item =
(plc4c_response_value_item *)write_item_element->value;
// do not delete the plc4c_item
// we also, in THIS case don't delete the random value which isn't really
// a pointer
// free(value_item->value);
value_item->value = NULL;
}
void plc4c_driver_simulated_free_write_response(
plc4c_write_response *response) {
// the request will be cleaned up elsewhere
plc4c_utils_list_delete_elements(response->response_items,
&plc4c_driver_simulated_free_write_response_item);
}
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_encode_address;
driver->connect_function = &plc4c_driver_simulated_connect_function;
driver->disconnect_function = &plc4c_driver_simulated_disconnect_function;
driver->read_function = &plc4c_driver_simulated_read_function;
driver->write_function = &plc4c_driver_simulated_write_function;
// TODO: Implement ...
driver->subscribe_function = NULL;
// TODO: Implement ...
driver->unsubscribe_function = NULL;
driver->free_read_response_function =
&plc4c_driver_simulated_free_read_response;
driver->free_write_response_function =
&plc4c_driver_simulated_free_write_response;
// TODO: Implement ...
driver->free_subscription_response_function = NULL;
// TODO: Implement ...
driver->free_unsubscription_response_function = NULL;
return driver;
}