lpcmanager

d8b12828e6aa
Enable IEC60870 server data loading and starting on PLC
/* Generated IEC 60870-5-104 (CS104) server runtime — instance %(locstr)s */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include "iec_types_all.h"
#include "iec60870_common.h"
#include "cs104_slave.h"
#include "cs101_information_objects.h"
#include "IEC104_%(locstr)s.h"
/* Referenced from __init_* before definition; silences -Wimplicit-function-declaration */
int __cleanup_%(locstr)s(void);
/* Filled when PLC located symbols are unavailable (empty LOCATED_VARIABLES.h etc.) */
%(diag_static_block)s
struct iec60870_binding {
int server_index;
int type_id;
int ioa;
int is_command;
int bind_kind;
void *iec_var;
};
typedef struct {
const char *loc_label;
int common_address;
char ip_str[128];
int port;
int max_open;
int ca_sz;
int ioa_sz;
int cot_two_byte;
int oa;
int apci_k, apci_w, t0, t1, t2, t3;
UDINT *rd_ctr;
UDINT *wr_ctr;
BOOL *conn_bool;
CS104_Slave slave;
int init_st;
} iec60870_srv_t;
static struct iec60870_binding iec60870_bindings[] = {
%(bindings_rows)s
};
static const size_t iec60870_binding_count = IEC60870_NUM_BINDINGS_%(locstr)s;
%(extern_block)s
%(srv_def)s
static void iec_rd_inc(iec60870_srv_t *s) {
if (s->rd_ctr)
*s->rd_ctr += 1U;
}
static void iec_wr_inc(iec60870_srv_t *s) {
if (s->wr_ctr)
*s->wr_ctr += 1U;
}
static bool iec_get_bool_var(void *p) {
return *(IEC_BOOL *)p != 0;
}
static void iec_set_bool_var(void *p, bool v) {
*(IEC_BOOL *)p = v ? (IEC_BOOL)1 : (IEC_BOOL)0;
}
static InformationObject iec_make_monitor_io(int type_id, int ioa, struct iec60870_binding *b) {
void *mem = malloc((size_t)InformationObject_getMaxSizeInMemory());
if (!mem)
return NULL;
QualityDescriptor q = IEC60870_QUALITY_GOOD;
switch (type_id) {
case M_SP_NA_1: {
bool val = iec_get_bool_var(b->iec_var);
return (InformationObject)SinglePointInformation_create(
(SinglePointInformation)mem, ioa, val, q);
}
case M_DP_NA_1: {
DoublePointValue dv = (DoublePointValue)(*(IEC_BYTE *)b->iec_var & (IEC_BYTE)3);
return (InformationObject)DoublePointInformation_create(
(DoublePointInformation)mem, ioa, dv, q);
}
case M_ST_NA_1: {
int sv = (int)*(IEC_INT *)b->iec_var;
return (InformationObject)StepPositionInformation_create(
(StepPositionInformation)mem, ioa, sv, false, q);
}
case M_ME_NA_1: {
float nv = NormalizedValue_fromScaled(
(int)(int16_t)*(IEC_UINT *)b->iec_var);
return (InformationObject)MeasuredValueNormalized_create(
(MeasuredValueNormalized)mem, ioa, nv, q);
}
case M_ME_NB_1: {
int sv = (int)*(IEC_INT *)b->iec_var;
return (InformationObject)MeasuredValueScaled_create(
(MeasuredValueScaled)mem, ioa, sv, q);
}
case M_ME_NC_1: {
float fv = *(IEC_REAL *)b->iec_var;
return (InformationObject)MeasuredValueShort_create(
(MeasuredValueShort)mem, ioa, fv, q);
}
default:
free(mem);
return NULL;
}
}
static bool iec_send_monitor(IMasterConnection conn, iec60870_srv_t *srv, struct iec60870_binding *b) {
CS101_AppLayerParameters al = CS104_Slave_getAppLayerParameters(srv->slave);
int oa = srv->cot_two_byte ? srv->oa : 0;
InformationObject io = iec_make_monitor_io(b->type_id, b->ioa, b);
if (!io)
return false;
CS101_ASDU asdu = CS101_ASDU_create(al, false, CS101_COT_INTERROGATED_BY_STATION, oa,
srv->common_address, false, false);
if (!asdu) {
InformationObject_destroy(io);
return false;
}
if (!CS101_ASDU_addInformationObject(asdu, io)) {
InformationObject_destroy(io);
CS101_ASDU_destroy(asdu);
return false;
}
if (!IMasterConnection_sendASDU(conn, asdu)) {
CS101_ASDU_destroy(asdu);
return false;
}
CS101_ASDU_destroy(asdu);
iec_rd_inc(srv);
return true;
}
static bool iec_interrogation_handler(void *parameter, IMasterConnection connection, CS101_ASDU asdu, uint8_t qoi) {
(void)asdu;
iec60870_srv_t *srv = (iec60870_srv_t *)parameter;
size_t idx = (size_t)(srv - iec60870_srv);
size_t i;
if (qoi == IEC60870_QOI_STATION) {
for (i = 0; i < iec60870_binding_count; i++) {
struct iec60870_binding *b = &iec60870_bindings[i];
if (b->server_index < 0)
continue;
if ((size_t)b->server_index != idx)
continue;
if (b->is_command)
continue;
iec_send_monitor(connection, srv, b);
}
}
return true;
}
static bool iec_handle_command(iec60870_srv_t *srv, IMasterConnection connection, CS101_ASDU asdu) {
TypeID tid = CS101_ASDU_getTypeID(asdu);
InformationObject io = CS101_ASDU_getElement(asdu, 0);
size_t srv_idx = (size_t)(srv - iec60870_srv);
size_t i;
struct iec60870_binding *match = NULL;
if (!io)
return false;
{
int ioa = InformationObject_getObjectAddress(io);
for (i = 0; i < iec60870_binding_count; i++) {
if (iec60870_bindings[i].server_index < 0)
continue;
if ((size_t)iec60870_bindings[i].server_index != srv_idx)
continue;
if (!iec60870_bindings[i].is_command)
continue;
if (iec60870_bindings[i].ioa == ioa && iec60870_bindings[i].type_id == (int)tid) {
match = &iec60870_bindings[i];
break;
}
}
}
if (!match)
return false;
switch (tid) {
case C_SC_NA_1: {
SingleCommand sc = (SingleCommand)io;
iec_set_bool_var(match->iec_var, SingleCommand_getState(sc));
break;
}
case C_DC_NA_1: {
DoubleCommand dc = (DoubleCommand)io;
*(IEC_BYTE *)match->iec_var = (IEC_BYTE)(DoubleCommand_getState(dc) & 0xff);
break;
}
case C_RC_NA_1: {
StepCommand sc = (StepCommand)io;
*(IEC_BYTE *)match->iec_var = (IEC_BYTE)((int)StepCommand_getState(sc) & 0xff);
break;
}
case C_SE_NA_1: {
SetpointCommandNormalized sn = (SetpointCommandNormalized)io;
{
float fv = SetpointCommandNormalized_getValue(sn);
int scv = NormalizedValue_toScaled(fv);
*(IEC_UINT *)match->iec_var = (uint16_t)scv;
}
break;
}
case C_SE_NB_1: {
SetpointCommandScaled ss = (SetpointCommandScaled)io;
*(IEC_INT *)match->iec_var = (int16_t)SetpointCommandScaled_getValue(ss);
break;
}
case C_SE_NC_1: {
SetpointCommandShort sf = (SetpointCommandShort)io;
*(IEC_REAL *)match->iec_var = SetpointCommandShort_getValue(sf);
break;
}
default:
return false;
}
iec_wr_inc(srv);
IMasterConnection_sendACT_CON(connection, asdu, false);
return true;
}
static bool iec_asdu_handler(void *parameter, IMasterConnection connection, CS101_ASDU asdu) {
iec60870_srv_t *srv = (iec60870_srv_t *)parameter;
TypeID tid = CS101_ASDU_getTypeID(asdu);
if ((int)tid >= C_SC_NA_1 && (int)tid <= C_SE_NC_1)
return iec_handle_command(srv, connection, asdu);
iec_rd_inc(srv);
return false;
}
static void iec_connection_event(void *parameter, IMasterConnection connection, CS104_PeerConnectionEvent event) {
(void)connection;
iec60870_srv_t *srv = (iec60870_srv_t *)parameter;
if (srv->conn_bool) {
if (event == CS104_CON_EVENT_ACTIVATED)
iec_set_bool_var(srv->conn_bool, true);
else if (event == CS104_CON_EVENT_DEACTIVATED || event == CS104_CON_EVENT_CONNECTION_CLOSED)
iec_set_bool_var(srv->conn_bool, CS104_Slave_getOpenConnections(srv->slave) > 0);
}
}
int __init_%(locstr)s(int argc, char **argv) {
int si;
(void)argc;
(void)argv;
for (si = 0; si < IEC60870_NUM_SERVERS_%(locstr)s; si++) {
iec60870_srv_t *s = &iec60870_srv[si];
s->slave = CS104_Slave_create(200, 200);
if (!s->slave) {
fprintf(stderr, "IEC60870 %%s: CS104_Slave_create failed\\n", s->loc_label);
goto fail;
}
CS104_Slave_setLocalAddress(s->slave, s->ip_str);
CS104_Slave_setLocalPort(s->slave, s->port);
CS104_Slave_setMaxOpenConnections(s->slave, s->max_open);
{
CS101_AppLayerParameters al = CS104_Slave_getAppLayerParameters(s->slave);
al->sizeOfCA = s->ca_sz;
al->sizeOfIOA = s->ioa_sz;
al->sizeOfCOT = s->cot_two_byte ? 2 : 1;
al->originatorAddress = s->oa;
}
{
CS104_APCIParameters ap = CS104_Slave_getConnectionParameters(s->slave);
ap->k = s->apci_k;
ap->w = s->apci_w;
ap->t0 = s->t0;
ap->t1 = s->t1;
ap->t2 = s->t2;
ap->t3 = s->t3;
}
CS104_Slave_setInterrogationHandler(s->slave, iec_interrogation_handler, s);
CS104_Slave_setASDUHandler(s->slave, iec_asdu_handler, s);
CS104_Slave_setConnectionEventHandler(s->slave, iec_connection_event, s);
CS104_Slave_start(s->slave);
s->init_st = 1;
}
return 0;
fail:
__cleanup_%(locstr)s();
return -1;
}
void __retrieve_%(locstr)s(void) {
int si;
for (si = 0; si < IEC60870_NUM_SERVERS_%(locstr)s; si++) {
iec60870_srv_t *s = &iec60870_srv[si];
if (s->slave && s->conn_bool)
iec_set_bool_var(s->conn_bool, CS104_Slave_getOpenConnections(s->slave) > 0);
}
}
void __publish_%(locstr)s(void) {
}
int __cleanup_%(locstr)s(void) {
int si;
for (si = 0; si < IEC60870_NUM_SERVERS_%(locstr)s; si++) {
iec60870_srv_t *s = &iec60870_srv[si];
if (s->slave) {
CS104_Slave_stop(s->slave);
CS104_Slave_destroy(s->slave);
s->slave = NULL;
}
s->init_st = 0;
}
return 0;
}