Extending modbus adapter to support linux

This commit is contained in:
Dipannita Shaw 2019-11-19 21:10:24 -08:00
Родитель cef6def7f1
Коммит 0671c21000
15 изменённых файлов: 2801 добавлений и 2622 удалений

Просмотреть файл

@ -13,9 +13,9 @@ endif()
compileAsC99()
add_subdirectory(serial_pnp)
add_subdirectory(modbus_pnp)
IF(WIN32)
add_subdirectory(modbus_pnp)
add_subdirectory(core_device_health)
add_subdirectory(camera)
ENDIF(WIN32)

Просмотреть файл

@ -4,10 +4,11 @@
#include "ModbusPnp.h"
#include "ModbusCapability.h"
#include "ModbusConnection\ModbusConnection.h"
#include "ModbusConnection/ModbusConnection.h"
#include "azure_c_shared_utility/condition.h"
static bool ModbusPnP_ContinueReadTasks = false;
static CONDITION_VARIABLE StopPolling;
static COND_HANDLE StopPolling;
#pragma region Commands
@ -92,7 +93,7 @@ ModbusPnp_CommandHandler(
if (0 < resultLength)
{
dtClientCommandResponseContext->responseData = calloc(resultLength + 1, sizeof(char));
memcpy_s((void*)dtClientCommandResponseContext->responseData, resultLength, resultedData, resultLength);
memcpy((void*)dtClientCommandResponseContext->responseData, resultedData, resultLength);
dtClientCommandResponseContext->responseDataLen = resultLength;
}
@ -128,17 +129,16 @@ const ModbusProperty* ModbusPnp_LookupProperty(SINGLYLINKEDLIST_HANDLE interface
return NULL;
}
int ModbusPnp_PropertyHandler(const DIGITALTWIN_CLIENT_PROPERTY_UPDATE* dtClientPropertyUpdate, void* userContextCallback)
void ModbusPnp_PropertyHandler(const DIGITALTWIN_CLIENT_PROPERTY_UPDATE* dtClientPropertyUpdate, void* userContextCallback)
//PMODBUS_DEVICE_CONTEXT modbusDevice, const char* propertyName, char* data, char** response)
{
PMODBUS_DEVICE_CONTEXT modbusDevice = (PMODBUS_DEVICE_CONTEXT)userContextCallback;
const ModbusProperty* property = ModbusPnp_LookupProperty(modbusDevice->InterfaceDefinitions, dtClientPropertyUpdate->propertyName, 0);
if (property == NULL) {
return -1;
return;
}
int resultLength = -1;
byte resultedData[MODBUS_RESPONSE_MAX_LENGTH];
memset(resultedData, 0x00, MODBUS_RESPONSE_MAX_LENGTH);
@ -148,15 +148,9 @@ int ModbusPnp_PropertyHandler(const DIGITALTWIN_CLIENT_PROPERTY_UPDATE* dtClient
capContext->connectionType = modbusDevice->DeviceConfig->ConnectionType;
capContext->hLock= modbusDevice->hConnectionLock;
resultLength = ModbusPnp_WriteToCapability(capContext, Property, (char*)dtClientPropertyUpdate->propertyDesired, resultedData);
if (resultLength > 0) {
//*response = calloc(resultLength + 1, sizeof(char)); //free in ModbusPnp_PropertyUpdateHandler
//memcpy_s(*response, resultLength, resultedData, resultLength);
}
ModbusPnp_WriteToCapability(capContext, Property, (char*)dtClientPropertyUpdate->propertyDesired, resultedData);
free(capContext);
return resultLength;
}
#pragma endregion
@ -189,15 +183,17 @@ int ModbusPnP_ReportReadOnlyProperty(DIGITALTWIN_INTERFACE_CLIENT_HANDLE pnpInte
return result;
}
int ModbusPnp_PollingSingleProperty(CapabilityContext *context)
int ModbusPnp_PollingSingleProperty(void *param)
{
CapabilityContext* context = param;
ModbusProperty* property = context->capability;
LogInfo("Start polling task for property \"%s\".", property->Name);
byte resultedData[MODBUS_RESPONSE_MAX_LENGTH];
memset(resultedData, 0x00, MODBUS_RESPONSE_MAX_LENGTH);
SRWLOCK lock;
AcquireSRWLockShared(&lock);
LOCK_HANDLE lock;
lock = Lock_Init();
Lock(lock);
while (ModbusPnP_ContinueReadTasks)
{
@ -206,10 +202,10 @@ int ModbusPnp_PollingSingleProperty(CapabilityContext *context)
ModbusPnP_ReportReadOnlyProperty(PnpAdapterInterface_GetPnpInterfaceClient(property->InterfaceClient), (char*)property->Name, (char*)resultedData);
}
SleepConditionVariableSRW(&StopPolling, &lock, property->DefaultFrequency, CONDITION_VARIABLE_LOCKMODE_SHARED);
Condition_Wait(StopPolling, lock, property->DefaultFrequency);
}
ReleaseSRWLockShared(&lock);
Unlock(lock);
LogInfo("Stopped polling task for property \"%s\".", property->Name);
free(context);
@ -248,14 +244,16 @@ int ModbusPnp_ReportTelemetry(DIGITALTWIN_INTERFACE_CLIENT_HANDLE pnpInterface,
return result;
}
int ModbusPnp_PollingSingleTelemetry(CapabilityContext *context)
int ModbusPnp_PollingSingleTelemetry(void *param)
{
CapabilityContext* context = param;
ModbusTelemetry* telemetry = context->capability;
LogInfo("Start polling task for telemetry \"%s\".", telemetry->Name);
byte resultedData[MODBUS_RESPONSE_MAX_LENGTH];
SRWLOCK lock;
AcquireSRWLockShared(&lock);
LOCK_HANDLE lock;
lock = Lock_Init();
Lock(lock);
while (ModbusPnP_ContinueReadTasks)
{
memset(resultedData, 0x00, MODBUS_RESPONSE_MAX_LENGTH);
@ -264,9 +262,9 @@ int ModbusPnp_PollingSingleTelemetry(CapabilityContext *context)
ModbusPnp_ReportTelemetry(PnpAdapterInterface_GetPnpInterfaceClient(telemetry->InterfaceClient), (char*)telemetry->Name, (char*)resultedData);
}
SleepConditionVariableSRW(&StopPolling, &lock, telemetry->DefaultFrequency, CONDITION_VARIABLE_LOCKMODE_SHARED);
Condition_Wait(StopPolling, lock, telemetry->DefaultFrequency);
}
ReleaseSRWLockShared(&lock);
Unlock(lock);
LogInfo("Stopped polling task for telemetry \"%s\".", telemetry->Name);
free(context);
@ -279,7 +277,7 @@ int ModbusPnp_PollingSingleTelemetry(CapabilityContext *context)
void StopPollingTasks()
{
ModbusPnP_ContinueReadTasks = false;
WakeAllConditionVariable(&StopPolling);;
Condition_Post(StopPolling);
}
int ModbusPnp_StartPollingAllTelemetryProperty(void* context)
@ -307,7 +305,7 @@ int ModbusPnp_StartPollingAllTelemetryProperty(void* context)
}
}
InitializeConditionVariable(&StopPolling);
StopPolling = Condition_Init();
ModbusPnP_ContinueReadTasks = true;
for (int i = 0; i < telemetryCount; i++)
@ -330,7 +328,7 @@ int ModbusPnp_StartPollingAllTelemetryProperty(void* context)
if (ThreadAPI_Create(&(deviceContext->PollingTasks[i]), ModbusPnp_PollingSingleTelemetry, (void*)pollingPayload) != THREADAPI_OK)
{
LogError("Failed to create worker thread for telemetry \"%s\", 0x%x", telemetry->Name, GetLastError());
LogError("Failed to create worker thread for telemetry \"%s\".", telemetry->Name);
}
}
@ -354,7 +352,7 @@ int ModbusPnp_StartPollingAllTelemetryProperty(void* context)
if (ThreadAPI_Create(&(deviceContext->PollingTasks[telemetryCount + i]), ModbusPnp_PollingSingleProperty, (void*)pollingPayload) != THREADAPI_OK)
{
LogError("Failed to create worker thread for proerty \"%s\", 0x%x", property->Name, GetLastError());
LogError("Failed to create worker thread for proerty \"%s\".", property->Name);
}
}

Просмотреть файл

@ -5,11 +5,22 @@ extern "C"
{
#endif
#include <Windows.h>
#include <cfgmgr32.h>
#include <ctype.h>
#include <digitaltwin_interface_client.h>
#include <stdint.h>
#ifdef WIN32
#include <Windows.h>
#include <cfgmgr32.h>
#else
#include <string.h>
typedef unsigned int DWORD;
typedef uint8_t BYTE;
typedef int HANDLE;
typedef short USHORT;
typedef uint16_t UINT16;
typedef unsigned char byte;
#endif
#include <digitaltwin_interface_client.h>
#include "azure_c_shared_utility/lock.h"
#include "ModbusConnection/ModbusConnectionHelper.h"
typedef enum ModbusAccessType
@ -62,7 +73,7 @@ typedef struct ModbusCommand {
typedef struct CapabilityContext {
void* capability;
HANDLE hDevice;
HANDLE hLock;
LOCK_HANDLE hLock;
MODBUS_CONNECTION_TYPE connectionType;
}CapabilityContext;
@ -72,7 +83,7 @@ void StopPollingTasks();
void ModbusPnp_CommandHandler(const DIGITALTWIN_CLIENT_COMMAND_REQUEST* dtClientCommandContext,
DIGITALTWIN_CLIENT_COMMAND_RESPONSE* dtClientCommandResponseContext, void* userContextCallback);
int ModbusPnp_PropertyHandler(const DIGITALTWIN_CLIENT_PROPERTY_UPDATE* dtClientPropertyUpdate, void* userContextCallback);
void ModbusPnp_PropertyHandler(const DIGITALTWIN_CLIENT_PROPERTY_UPDATE* dtClientPropertyUpdate, void* userContextCallback);
#ifdef __cplusplus

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -14,8 +14,7 @@ extern "C"
// ModbusConnection "Public" methods
bool ModbusPnp_CloseDevice(MODBUS_CONNECTION_TYPE connectionType, HANDLE *hDevice, HANDLE lock);
bool ModbusPnp_CloseDevice(MODBUS_CONNECTION_TYPE connectionType, HANDLE hDevice, LOCK_HANDLE lock);
int ModbusPnp_SetReadRequest(ModbusDeviceConfig* deviceConfig, CapabilityType capabilityType, void* capability);
int ModbusPnp_ReadCapability(CapabilityContext* capabilityContext, CapabilityType capabilityType, byte* resultedData);
int ModbusPnp_WriteToCapability(CapabilityContext* capabilityContext, CapabilityType capabilityType, char* requestStr, byte* resultedData);

Просмотреть файл

@ -1,70 +1,71 @@
#include "ModbusConnectionHelper.h"
bool ModbusConnectionHelper_GetFunctionCode(const char* startAddress, bool isRead, byte* functionCode, UINT16* modbusAddress)
{
char entityChar = startAddress[0];
byte entityType = (byte)atoi(&entityChar);
switch (entityType)
{
case CoilStatus:
{
*functionCode = isRead ? ReadCoils : WriteCoil;
break;
}
case InputStatus:
{
*functionCode = ReadInputs;
break;
}
case HoldingRegister:
{
*functionCode = isRead ? ReadHoldingRegisters : WriteHoldingRegister;
break;
}
case InputRegister:
{
*functionCode = ReadInputRegisters;
break;
}
default:
return false;
}
char subAddress[5] = "";
strncpy_s(subAddress, 5, &(startAddress[1]), 4);
*modbusAddress = (UINT16)(atoi(subAddress) - 1);
return true;
}
bool ModbusConnectionHelper_ConvertValueStrToUInt16(ModbusDataType dataType, FunctionCodeType functionCodeType, char* valueStr, UINT16* value)
{
switch (dataType)
{
case FLAG:
if (functionCodeType <= ReadInputs || functionCodeType == WriteCoil)
{
if (strcmp(valueStr, "true") == 0)
{
//ON
*value = 0xFF00;
}
else
{
//OFF
*value = 0x0000;
}
}
return true;
case NUMERIC:
*value = (UINT16)atoi(valueStr);
return true;
case STRING:
default:
break;
}
return false;
}
#include "ModbusConnectionHelper.h"
#include <stdlib.h>
bool ModbusConnectionHelper_GetFunctionCode(const char* startAddress, bool isRead, uint8_t* functionCode, uint16_t* modbusAddress)
{
char entityChar = startAddress[0];
uint8_t entityType = (uint8_t)atoi(&entityChar);
switch (entityType)
{
case CoilStatus:
{
*functionCode = isRead ? ReadCoils : WriteCoil;
break;
}
case InputStatus:
{
*functionCode = ReadInputs;
break;
}
case HoldingRegister:
{
*functionCode = isRead ? ReadHoldingRegisters : WriteHoldingRegister;
break;
}
case InputRegister:
{
*functionCode = ReadInputRegisters;
break;
}
default:
return false;
}
char subAddress[5] = "";
strncpy_s(subAddress, 5, &(startAddress[1]), 4);
*modbusAddress = (uint16_t)(atoi(subAddress) - 1);
return true;
}
bool ModbusConnectionHelper_ConvertValueStrToUInt16(ModbusDataType dataType, FunctionCodeType functionCodeType, char* valueStr, uint16_t* value)
{
switch (dataType)
{
case FLAG:
if (functionCodeType <= ReadInputs || functionCodeType == WriteCoil)
{
if (strcmp(valueStr, "true") == 0)
{
//ON
*value = 0xFF00;
}
else
{
//OFF
*value = 0x0000;
}
}
return true;
case NUMERIC:
*value = (uint16_t)atoi(valueStr);
return true;
case STRING:
default:
break;
}
return false;
}

Просмотреть файл

@ -1,98 +1,95 @@
#pragma once
#ifdef __cplusplus
extern "C"
{
#endif
#include <Windows.h>
#include <cfgmgr32.h>
#include <digitaltwin_interface_client.h>
#include "../ModbusEnum.h"
#define MODBUS_EXCEPTION_CODE 0x80
#define MODBUS_RESPONSE_MAX_LENGTH 32
// Modbus Operation
typedef struct _MODBUS_TCP_MBAP_HEADER
{
BYTE transacID_Hi; // Hight byte of Transaction ID: used for transaction pairing.
BYTE TransacID_Lo; // Low byte of Transaction ID
BYTE ProtocolID_Hi; // Hight byte of Protocol ID: 0 for Modbus
BYTE ProtocolID_Lo; // Low byte of Protocol ID: 0 for Modbus
BYTE Length_Hi; // Hight byte of Length of following field: Unit ID + PDU (Data field)
BYTE Length_Lo; // Low byte of lendth Length.
BYTE UnitID; // Unit ID: used for intra-system routing purpose.
} MODBUS_TCP_MBAP_HEADER;
// Read request
typedef struct _MODBUS_READ_PAYLOAD
{
UINT8 FunctionCode; // Function Code
UINT8 StartAddr_Hi; // High byte for starting address
UINT8 StartAddr_Lo; // Low byte for starting address
UINT8 ReadLen_Hi; // High byte of Number of registers to read
UINT8 ReadLen_Lo; // Low byte of Number of registers to read
} MODBUS_READ_REG_PAYLOAD;
typedef struct _MODBUS_TCP_READ_REG_REQUEST
{
MODBUS_TCP_MBAP_HEADER MBAP; // MBAP header for Modbus TCP/IP
MODBUS_READ_REG_PAYLOAD Payload; // Request payload
} MODBUS_TCP_READ_REG_REQUEST;
typedef struct _MODBUS_RTU_READ_REQUEST
{
UINT8 UnitID; // Unit ID: slave address for the Modbus device.
MODBUS_READ_REG_PAYLOAD Payload; // Request payload
UINT16 CRC; // CRC
} MODBUS_RTU_READ_REQUEST;
typedef union _MODBUS_READ_REQUEST
{
byte TcpArr[sizeof(MODBUS_TCP_READ_REG_REQUEST)];
byte RtuArr[sizeof(MODBUS_RTU_READ_REQUEST)];
MODBUS_TCP_READ_REG_REQUEST TcpRequest;
MODBUS_RTU_READ_REQUEST RtuRequest;
}MODBUS_READ_REQUEST;
// Write Signle Value request
typedef struct _MODBUS_WRITE_1_REG_PAYLOAD
{
UINT8 FunctionCode; // Function Code
UINT8 RegAddr_Hi; // High byte for starting address
UINT8 RegAddr_Lo; // Low byte for starting address
UINT8 Value_Hi; // High byte of Number of registers to read
UINT8 Value_Lo; // Low byte of Number of registers to read
} MODBUS_WRITE_1_REG_PAYLOAD;
typedef struct _MODBUS_TCP_WRITE_1_REG_REQUEST
{
MODBUS_TCP_MBAP_HEADER MBAP; // MBAP header for Modbus TCP/IP
MODBUS_WRITE_1_REG_PAYLOAD Payload; // Request payload
} MODBUS_TCP_WRITE_1_REG_REQUEST;
typedef struct _MODBUS_RTU_WRITE_1_REG_REQUEST
{
UINT8 UnitID; // Unit ID: slave address for the Modbus device.
MODBUS_WRITE_1_REG_PAYLOAD Payload; // Request payload
UINT16 CRC; // CRC
} MODBUS_RTU_WRITE_1_REG_REQUEST;
typedef union _MODBUS_WRITE_1_REG_REQUEST
{
byte TcpArr[sizeof(MODBUS_TCP_WRITE_1_REG_REQUEST)];
byte RtuArr[sizeof(MODBUS_RTU_WRITE_1_REG_REQUEST)];
MODBUS_TCP_WRITE_1_REG_REQUEST TcpRequest;
MODBUS_RTU_WRITE_1_REG_REQUEST RtuRequest;
}MODBUS_WRITE_1_REG_REQUEST;
#pragma region functions
bool ModbusConnectionHelper_GetFunctionCode(const char* startAddress, bool isRead, byte* functionCode, UINT16* modbusAddress);
bool ModbusConnectionHelper_ConvertValueStrToUInt16(ModbusDataType dataType, FunctionCodeType functionCodeType, char* valueStr, UINT16* value);
#pragma endregion
#ifdef __cplusplus
}
#endif
#pragma once
#ifdef __cplusplus
extern "C"
{
#endif
#include <digitaltwin_interface_client.h>
#include "../ModbusEnum.h"
#define MODBUS_EXCEPTION_CODE 0x80
#define MODBUS_RESPONSE_MAX_LENGTH 32
// Modbus Operation
typedef struct _MODBUS_TCP_MBAP_HEADER
{
uint8_t transacID_Hi; // Hight uint8_t of Transaction ID: used for transaction pairing.
uint8_t TransacID_Lo; // Low uint8_t of Transaction ID
uint8_t ProtocolID_Hi; // Hight uint8_t of Protocol ID: 0 for Modbus
uint8_t ProtocolID_Lo; // Low uint8_t of Protocol ID: 0 for Modbus
uint8_t Length_Hi; // Hight uint8_t of Length of following field: Unit ID + PDU (Data field)
uint8_t Length_Lo; // Low uint8_t of lendth Length.
uint8_t UnitID; // Unit ID: used for intra-system routing purpose.
} MODBUS_TCP_MBAP_HEADER;
// Read request
typedef struct _MODBUS_READ_PAYLOAD
{
uint8_t FunctionCode; // Function Code
uint8_t StartAddr_Hi; // High uint8_t for starting address
uint8_t StartAddr_Lo; // Low uint8_t for starting address
uint8_t ReadLen_Hi; // High uint8_t of Number of registers to read
uint8_t ReadLen_Lo; // Low uint8_t of Number of registers to read
} MODBUS_READ_REG_PAYLOAD;
typedef struct _MODBUS_TCP_READ_REG_REQUEST
{
MODBUS_TCP_MBAP_HEADER MBAP; // MBAP header for Modbus TCP/IP
MODBUS_READ_REG_PAYLOAD Payload; // Request payload
} MODBUS_TCP_READ_REG_REQUEST;
typedef struct _MODBUS_RTU_READ_REQUEST
{
uint8_t UnitID; // Unit ID: slave address for the Modbus device.
MODBUS_READ_REG_PAYLOAD Payload; // Request payload
uint16_t CRC; // CRC
} MODBUS_RTU_READ_REQUEST;
typedef union _MODBUS_READ_REQUEST
{
uint8_t TcpArr[sizeof(MODBUS_TCP_READ_REG_REQUEST)];
uint8_t RtuArr[sizeof(MODBUS_RTU_READ_REQUEST)];
MODBUS_TCP_READ_REG_REQUEST TcpRequest;
MODBUS_RTU_READ_REQUEST RtuRequest;
}MODBUS_READ_REQUEST;
// Write Signle Value request
typedef struct _MODBUS_WRITE_1_REG_PAYLOAD
{
uint8_t FunctionCode; // Function Code
uint8_t RegAddr_Hi; // High uint8_t for starting address
uint8_t RegAddr_Lo; // Low uint8_t for starting address
uint8_t Value_Hi; // High uint8_t of Number of registers to read
uint8_t Value_Lo; // Low uint8_t of Number of registers to read
} MODBUS_WRITE_1_REG_PAYLOAD;
typedef struct _MODBUS_TCP_WRITE_1_REG_REQUEST
{
MODBUS_TCP_MBAP_HEADER MBAP; // MBAP header for Modbus TCP/IP
MODBUS_WRITE_1_REG_PAYLOAD Payload; // Request payload
} MODBUS_TCP_WRITE_1_REG_REQUEST;
typedef struct _MODBUS_RTU_WRITE_1_REG_REQUEST
{
uint8_t UnitID; // Unit ID: slave address for the Modbus device.
MODBUS_WRITE_1_REG_PAYLOAD Payload; // Request payload
uint16_t CRC; // CRC
} MODBUS_RTU_WRITE_1_REG_REQUEST;
typedef union _MODBUS_WRITE_1_REG_REQUEST
{
uint8_t TcpArr[sizeof(MODBUS_TCP_WRITE_1_REG_REQUEST)];
uint8_t RtuArr[sizeof(MODBUS_RTU_WRITE_1_REG_REQUEST)];
MODBUS_TCP_WRITE_1_REG_REQUEST TcpRequest;
MODBUS_RTU_WRITE_1_REG_REQUEST RtuRequest;
}MODBUS_WRITE_1_REG_REQUEST;
#pragma region functions
bool ModbusConnectionHelper_GetFunctionCode(const char* startAddress, bool isRead, uint8_t* functionCode, uint16_t* modbusAddress);
bool ModbusConnectionHelper_ConvertValueStrToUInt16(ModbusDataType dataType, FunctionCodeType functionCodeType, char* valueStr, uint16_t* value);
#pragma endregion
#ifdef __cplusplus
}
#endif

Просмотреть файл

@ -1,260 +1,306 @@
#include "azure_c_shared_utility/threadapi.h"
#include "ModbusRtuConnection.h"
UINT16 GetCRC(byte *message, size_t length)
{
if (NULL == message)
{
// TODO: This was -1. Check what should be the correct return value.
return 0;
}
UINT16 crcFull = 0xFFFF;
for (int i = 0; i < (int)length; ++i)
{
crcFull = (UINT16)(crcFull ^ message[i]);
for (int j = 0; j < 8; ++j)
{
byte crcLsb = (byte)(crcFull & 0x0001);
crcFull = (UINT16)((crcFull >> 1) & 0x7FFF);
if (crcLsb == 1)
{
crcFull = (UINT16)(crcFull ^ 0xA001);
}
}
}
return crcFull;
}
int ModbusRtu_GetHeaderSize(void) {
return RTU_HEADER_SIZE;
}
bool ModbusRtu_CloseDevice(HANDLE hDevice, HANDLE hLock)
{
bool result = -1;
DWORD waitResult = WaitForSingleObject(hLock, INFINITE);
if (WAIT_ABANDONED == waitResult)
{
LogError("Device communicate lock is abandoned: %d", GetLastError());
return result;
}
result = CloseHandle(hDevice);
ReleaseMutex(hLock);
LogInfo("Serial Port Closed.");
return result;
}
int ModbusRtu_SetReadRequest(CapabilityType capabilityType, void* capability, byte unitId)
{
UINT16 modbusAddress = 0;
switch (capabilityType)
{
case Telemetry:
{
ModbusTelemetry* telemetry = (ModbusTelemetry*)capability;
if (!ModbusConnectionHelper_GetFunctionCode(telemetry->StartAddress, true, &(telemetry->ReadRequest.RtuRequest.Payload.FunctionCode), &modbusAddress))
{
LogError("Failed to get Modbus function code for telemetry \"%s\".", telemetry->Name);
return -1;
}
telemetry->ReadRequest.RtuRequest.UnitID = unitId;
telemetry->ReadRequest.RtuRequest.Payload.StartAddr_Hi = (modbusAddress >> 8) & 0xff;
telemetry->ReadRequest.RtuRequest.Payload.StartAddr_Lo = modbusAddress & 0xff;
telemetry->ReadRequest.RtuRequest.Payload.ReadLen_Hi = (telemetry->Length >> 8) & 0xff;
telemetry->ReadRequest.RtuRequest.Payload.ReadLen_Lo = telemetry->Length & 0xff;
telemetry->ReadRequest.RtuRequest.CRC = GetCRC(telemetry->ReadRequest.RtuArr, RTU_REQUEST_SIZE - 2);
break;
}
case Property:
{
ModbusProperty* property = (ModbusProperty*)capability;
if (!ModbusConnectionHelper_GetFunctionCode(property->StartAddress, true, &(property->ReadRequest.RtuRequest.Payload.FunctionCode), &modbusAddress))
{
LogError("Failed to get Modbus function code for property \"%s\".", property->Name);
return -1;
}
property->ReadRequest.RtuRequest.UnitID = unitId;
property->ReadRequest.RtuRequest.Payload.StartAddr_Hi = (modbusAddress >> 8) & 0xff;
property->ReadRequest.RtuRequest.Payload.StartAddr_Lo = modbusAddress & 0xff;
property->ReadRequest.RtuRequest.Payload.ReadLen_Hi = (property->Length >> 8) & 0xff;
property->ReadRequest.RtuRequest.Payload.ReadLen_Lo = property->Length & 0xff;
property->ReadRequest.RtuRequest.CRC = GetCRC(property->ReadRequest.RtuArr, RTU_REQUEST_SIZE - 2);
break;
}
default:
LogError("Modbus read if not supported for the capability.");
return -1;
}
return 0;
}
int ModbusRtu_SetWriteRequest(CapabilityType capabilityType, void* capability, char* valueStr)
{
UINT16 binaryData = 0;
UINT16 modbusAddress = 0;
switch (capabilityType)
{
case Command:
{
ModbusCommand* command = (ModbusCommand*)capability;
if (!ModbusConnectionHelper_GetFunctionCode(command->StartAddress, false, &(command->WriteRequest.RtuRequest.Payload.FunctionCode), &modbusAddress))
{
LogError("Failed to get Modbus function code for command \"%s\".", command->Name);
return -1;
}
if (!ModbusConnectionHelper_ConvertValueStrToUInt16(command->DataType, command->WriteRequest.RtuRequest.Payload.FunctionCode, valueStr, &binaryData))
{
LogError("Failed to convert data \"%s\" to byte array command \"%s\".", valueStr, command->Name);
return -1;
}
command->WriteRequest.RtuRequest.Payload.RegAddr_Hi = (modbusAddress >> 8) & 0xff;
command->WriteRequest.RtuRequest.Payload.RegAddr_Lo = modbusAddress & 0xff;
command->WriteRequest.RtuRequest.Payload.Value_Hi = (binaryData >> 8) & 0xff;
command->WriteRequest.RtuRequest.Payload.Value_Lo = binaryData & 0xff;
command->WriteRequest.RtuRequest.CRC = GetCRC(command->WriteRequest.RtuArr, RTU_REQUEST_SIZE - 2);
break;
}
case Property:
{
ModbusProperty* property = (ModbusProperty*)capability;
if (!ModbusConnectionHelper_GetFunctionCode(property->StartAddress, false, &(property->WriteRequest.RtuRequest.Payload.FunctionCode), &modbusAddress))
{
LogError("Failed to get Modbus function code for property \"%s\".", property->Name);
return -1;
}
if (!ModbusConnectionHelper_ConvertValueStrToUInt16(property->DataType, property->WriteRequest.RtuRequest.Payload.FunctionCode, valueStr, &binaryData))
{
LogError("Failed to convert data \"%s\" to byte array command \"%s\".", valueStr, property->Name);
return -1;
}
property->WriteRequest.RtuRequest.Payload.RegAddr_Hi = (modbusAddress >> 8) & 0xff;
property->WriteRequest.RtuRequest.Payload.RegAddr_Lo = modbusAddress & 0xff;
property->WriteRequest.RtuRequest.Payload.Value_Hi = (binaryData >> 8) & 0xff;
property->WriteRequest.RtuRequest.Payload.Value_Lo = binaryData & 0xff;
property->WriteRequest.RtuRequest.CRC = GetCRC(property->WriteRequest.RtuArr, RTU_REQUEST_SIZE - 2);
break;
}
default:
LogError("Modbus read if not supported for the capability.");
return -1;
}
return 0;
}
int ModbusRtu_SendRequest(HANDLE handler, byte *requestArr, DWORD arrLen)
{
if (NULL == handler || NULL == requestArr)
{
LogError("Failed to send request: connection handler and/or the request array is NUlL.");
return -1;
}
DWORD totalBytesSent = 0;
BOOL result = WriteFile((HANDLE)handler,
requestArr,
arrLen,
&totalBytesSent,
NULL);
if (!result) {
//error setting serial port state
LogError("Failed to send request: %x", GetLastError());
return -1;
}
return totalBytesSent;
}
int ModbusRtu_ReadResponse(HANDLE handler, byte *response, DWORD arrLen)
{
if (NULL == handler || NULL == response)
{
LogError("Failed to read response: connection handler and/or the request array is NUlL.");
return -1;
}
BOOL result = false;
DWORD bytesReceived = 0;
DWORD totalBytesReceived = 0;
int retry = 0;
const int retryLimit = 3;
while (totalBytesReceived < RTU_HEADER_SIZE && retry < retryLimit)
{
result = ReadFile(handler,
response + totalBytesReceived,
arrLen - totalBytesReceived - 1,
&bytesReceived,
NULL);
if (bytesReceived > 0)
{
totalBytesReceived += bytesReceived;
}
else
{
retry++;
ThreadAPI_Sleep(2 * 1000);
}
}
if (response[1] != WriteCoil && response[1] != WriteHoldingRegister)
{
// Get data length from the PDU
UINT16 length = (UINT16)(response[1] >= MODBUS_EXCEPTION_CODE ? 2 : response[2] + 2);
DWORD expectedResponseLength = length + RTU_HEADER_SIZE + 2; //HEADER_SIZE + CRC_SIZE;
if (expectedResponseLength != arrLen)
{
//TODO
//Array.Resize<byte>(ref response, expectedResponseLength);
}
while (totalBytesReceived < expectedResponseLength && retry < retryLimit)
{
result = ReadFile(handler,
response + totalBytesReceived,
arrLen - totalBytesReceived - 1,
&bytesReceived,
NULL);
if (bytesReceived > 0)
{
totalBytesReceived += bytesReceived;
}
else
{
retry++;
ThreadAPI_Sleep(2 * 1000);
}
}
}
if (!result)
{
LogError("Failed to read response: %x", GetLastError());
return -1;
}
return totalBytesReceived;
#include "azure_c_shared_utility/threadapi.h"
#include "ModbusRtuConnection.h"
#ifndef WIN32
#include <unistd.h>
#endif // WIN32
UINT16 GetCRC(byte *message, size_t length)
{
if (NULL == message)
{
// TODO: This was -1. Check what should be the correct return value.
return 0;
}
UINT16 crcFull = 0xFFFF;
for (int i = 0; i < (int)length; ++i)
{
crcFull = (UINT16)(crcFull ^ message[i]);
for (int j = 0; j < 8; ++j)
{
byte crcLsb = (byte)(crcFull & 0x0001);
crcFull = (UINT16)((crcFull >> 1) & 0x7FFF);
if (crcLsb == 1)
{
crcFull = (UINT16)(crcFull ^ 0xA001);
}
}
}
return crcFull;
}
int ModbusRtu_GetHeaderSize(void) {
return RTU_HEADER_SIZE;
}
bool ModbusRtu_CloseDevice(HANDLE hDevice, LOCK_HANDLE hLock)
{
bool result = -1;
if (LOCK_OK != Lock(hLock))
{
LogError("Device communicate lock could not be acquired.");
return result;
}
#ifdef WIN32
result = CloseHandle(hDevice);
#else
result = !(close(hDevice));
#endif
Unlock(hLock);
LogInfo("Serial Port Closed.");
return result;
}
int ModbusRtu_SetReadRequest(CapabilityType capabilityType, void* capability, byte unitId)
{
UINT16 modbusAddress = 0;
switch (capabilityType)
{
case Telemetry:
{
ModbusTelemetry* telemetry = (ModbusTelemetry*)capability;
if (!ModbusConnectionHelper_GetFunctionCode(telemetry->StartAddress, true, &(telemetry->ReadRequest.RtuRequest.Payload.FunctionCode), &modbusAddress))
{
LogError("Failed to get Modbus function code for telemetry \"%s\".", telemetry->Name);
return -1;
}
telemetry->ReadRequest.RtuRequest.UnitID = unitId;
telemetry->ReadRequest.RtuRequest.Payload.StartAddr_Hi = (modbusAddress >> 8) & 0xff;
telemetry->ReadRequest.RtuRequest.Payload.StartAddr_Lo = modbusAddress & 0xff;
telemetry->ReadRequest.RtuRequest.Payload.ReadLen_Hi = (telemetry->Length >> 8) & 0xff;
telemetry->ReadRequest.RtuRequest.Payload.ReadLen_Lo = telemetry->Length & 0xff;
telemetry->ReadRequest.RtuRequest.CRC = GetCRC(telemetry->ReadRequest.RtuArr, RTU_REQUEST_SIZE - 2);
break;
}
case Property:
{
ModbusProperty* property = (ModbusProperty*)capability;
if (!ModbusConnectionHelper_GetFunctionCode(property->StartAddress, true, &(property->ReadRequest.RtuRequest.Payload.FunctionCode), &modbusAddress))
{
LogError("Failed to get Modbus function code for property \"%s\".", property->Name);
return -1;
}
property->ReadRequest.RtuRequest.UnitID = unitId;
property->ReadRequest.RtuRequest.Payload.StartAddr_Hi = (modbusAddress >> 8) & 0xff;
property->ReadRequest.RtuRequest.Payload.StartAddr_Lo = modbusAddress & 0xff;
property->ReadRequest.RtuRequest.Payload.ReadLen_Hi = (property->Length >> 8) & 0xff;
property->ReadRequest.RtuRequest.Payload.ReadLen_Lo = property->Length & 0xff;
property->ReadRequest.RtuRequest.CRC = GetCRC(property->ReadRequest.RtuArr, RTU_REQUEST_SIZE - 2);
break;
}
default:
LogError("Modbus read if not supported for the capability.");
return -1;
}
return 0;
}
int ModbusRtu_SetWriteRequest(CapabilityType capabilityType, void* capability, char* valueStr)
{
UINT16 binaryData = 0;
UINT16 modbusAddress = 0;
switch (capabilityType)
{
case Command:
{
ModbusCommand* command = (ModbusCommand*)capability;
if (!ModbusConnectionHelper_GetFunctionCode(command->StartAddress, false, &(command->WriteRequest.RtuRequest.Payload.FunctionCode), &modbusAddress))
{
LogError("Failed to get Modbus function code for command \"%s\".", command->Name);
return -1;
}
if (!ModbusConnectionHelper_ConvertValueStrToUInt16(command->DataType, command->WriteRequest.RtuRequest.Payload.FunctionCode, valueStr, &binaryData))
{
LogError("Failed to convert data \"%s\" to byte array command \"%s\".", valueStr, command->Name);
return -1;
}
command->WriteRequest.RtuRequest.Payload.RegAddr_Hi = (modbusAddress >> 8) & 0xff;
command->WriteRequest.RtuRequest.Payload.RegAddr_Lo = modbusAddress & 0xff;
command->WriteRequest.RtuRequest.Payload.Value_Hi = (binaryData >> 8) & 0xff;
command->WriteRequest.RtuRequest.Payload.Value_Lo = binaryData & 0xff;
command->WriteRequest.RtuRequest.CRC = GetCRC(command->WriteRequest.RtuArr, RTU_REQUEST_SIZE - 2);
break;
}
case Property:
{
ModbusProperty* property = (ModbusProperty*)capability;
if (!ModbusConnectionHelper_GetFunctionCode(property->StartAddress, false, &(property->WriteRequest.RtuRequest.Payload.FunctionCode), &modbusAddress))
{
LogError("Failed to get Modbus function code for property \"%s\".", property->Name);
return -1;
}
if (!ModbusConnectionHelper_ConvertValueStrToUInt16(property->DataType, property->WriteRequest.RtuRequest.Payload.FunctionCode, valueStr, &binaryData))
{
LogError("Failed to convert data \"%s\" to byte array command \"%s\".", valueStr, property->Name);
return -1;
}
property->WriteRequest.RtuRequest.Payload.RegAddr_Hi = (modbusAddress >> 8) & 0xff;
property->WriteRequest.RtuRequest.Payload.RegAddr_Lo = modbusAddress & 0xff;
property->WriteRequest.RtuRequest.Payload.Value_Hi = (binaryData >> 8) & 0xff;
property->WriteRequest.RtuRequest.Payload.Value_Lo = binaryData & 0xff;
property->WriteRequest.RtuRequest.CRC = GetCRC(property->WriteRequest.RtuArr, RTU_REQUEST_SIZE - 2);
break;
}
default:
LogError("Modbus read if not supported for the capability.");
return -1;
}
return 0;
}
int ModbusRtu_SendRequest(HANDLE handler, byte *requestArr, DWORD arrLen)
{
#ifdef WIN32
if (NULL == handler || NULL == requestArr)
{
LogError("Failed to read response: connection handler and/or the request array is null.");
return -1;
}
#else
if (!handler || NULL == requestArr)
{
LogError("Failed to read response: connection handler and/or the request array is null.");
return -1;
}
#endif
DWORD totalBytesSent = 0;
#ifdef WIN32
if (!WriteFile((HANDLE)handler,
requestArr,
arrLen,
&totalBytesSent,
NULL))
{
LogError("Failed to send request: %x", GetLastError());
return -1;
}
#else
totalBytesSent = write(handler, requestArr, arrLen);
if (totalBytesSent != arrLen)
{
LogError("Failed to send request.");
return -1;
}
#endif
return totalBytesSent;
}
int ModbusRtu_ReadResponse(HANDLE handler, byte *response, DWORD arrLen)
{
#ifdef WIN32
if (NULL == handler || NULL == response)
{
LogError("Failed to read response: connection handler and/or the response is null.");
return -1;
}
#else
if (!handler || NULL == response)
{
LogError("Failed to read response: connection handler and/or the response is null.");
return -1;
}
#endif
bool result = false;
DWORD bytesReceived = 0;
DWORD totalBytesReceived = 0;
int retry = 0;
const int retryLimit = 3;
while (totalBytesReceived < RTU_HEADER_SIZE && retry < retryLimit)
{
#ifdef WIN32
result = (FALSE != ReadFile(handler,
(response + totalBytesReceived),
(arrLen - totalBytesReceived - 1),
&bytesReceived,
NULL));
#else
bytesReceived = read(handler, response, (arrLen - totalBytesReceived - 1));
result = (bytesReceived > 0);
#endif
if (bytesReceived > 0)
{
totalBytesReceived += bytesReceived;
}
else
{
retry++;
ThreadAPI_Sleep(2 * 1000);
}
}
if (response[1] != WriteCoil && response[1] != WriteHoldingRegister)
{
// Get data length from the PDU
UINT16 length = (UINT16)(response[1] >= MODBUS_EXCEPTION_CODE ? 2 : response[2] + 2);
DWORD expectedResponseLength = length + RTU_HEADER_SIZE + 2; //HEADER_SIZE + CRC_SIZE;
if (expectedResponseLength != arrLen)
{
//TODO
//Array.Resize<byte>(ref response, expectedResponseLength);
}
while (totalBytesReceived < expectedResponseLength && retry < retryLimit)
{
#ifdef WIN32
result = (FALSE != ReadFile(handler,
(response + totalBytesReceived),
(arrLen - totalBytesReceived - 1),
&bytesReceived,
NULL));
#else
bytesReceived = read(handler, response, (arrLen - totalBytesReceived - 1));
result = (bytesReceived > 0);
#endif
if (bytesReceived > 0)
{
totalBytesReceived += bytesReceived;
}
else
{
retry++;
ThreadAPI_Sleep(2 * 1000);
}
}
}
if (!result)
{
#ifdef WIN32
LogError("Failed to read response: %x.", GetLastError());
#else
LogError("Failed to read response.");
#endif
return -1;
}
return totalBytesReceived;
}

Просмотреть файл

@ -1,24 +1,24 @@
#pragma once
#ifdef __cplusplus
extern "C"
{
#endif
#include "azure_c_shared_utility/xlogging.h"
#include "../ModbusCapability.h"
#include "ModbusConnectionHelper.h"
#define RTU_REQUEST_SIZE 8
#define RTU_HEADER_SIZE 1
int ModbusRtu_GetHeaderSize(void);
bool ModbusRtu_CloseDevice(HANDLE hDevice, HANDLE lock);
int ModbusRtu_SetReadRequest(CapabilityType capabilityType, void* capability, byte unitId);
int ModbusRtu_SetWriteRequest(CapabilityType capabilityType, void* capability, char* valueStr);
int ModbusRtu_SendRequest(HANDLE handler, byte *requestArr, DWORD arrLen);
int ModbusRtu_ReadResponse(HANDLE handler, byte *response, DWORD arrLen);
#ifdef __cplusplus
}
#pragma once
#ifdef __cplusplus
extern "C"
{
#endif
#include "azure_c_shared_utility/xlogging.h"
#include "../ModbusCapability.h"
#include "ModbusConnectionHelper.h"
#define RTU_REQUEST_SIZE 8
#define RTU_HEADER_SIZE 1
int ModbusRtu_GetHeaderSize(void);
bool ModbusRtu_CloseDevice(HANDLE hDevice, LOCK_HANDLE lock);
int ModbusRtu_SetReadRequest(CapabilityType capabilityType, void* capability, byte unitId);
int ModbusRtu_SetWriteRequest(CapabilityType capabilityType, void* capability, char* valueStr);
int ModbusRtu_SendRequest(HANDLE handler, byte *requestArr, DWORD arrLen);
int ModbusRtu_ReadResponse(HANDLE handler, byte *response, DWORD arrLen);
#ifdef __cplusplus
}
#endif

Просмотреть файл

@ -1,244 +1,259 @@
#include "ModbusTCPConnection.h"
// timeout interval for waiting for socket readiness
#define SOCKET_TIMEOUT_SEC 5
#define SOCKET_TIMEOUT_USEC 0
struct timeval socket_timeout;
int ModbusTcp_GetHeaderSize(void) {
return TCP_HEADER_SIZE;
}
bool ModbusTcp_CloseDevice(SOCKET socket, HANDLE hLock)
{
DWORD waitResult = WaitForSingleObject(hLock, INFINITE);
if (WAIT_ABANDONED == waitResult)
{
LogError("Device communicate lock is abandoned: %d", GetLastError());
return false;
}
shutdown(socket, 1);
int byteRcvd = 1;
BYTE buffer[MODBUS_RESPONSE_MAX_LENGTH];
while (0 < byteRcvd)
{
byteRcvd = recv(socket, (char*)buffer, MODBUS_RESPONSE_MAX_LENGTH, 0);
}
closesocket(socket);
WSACleanup();
ReleaseMutex(hLock);
LogInfo("Socket Closed.");
return true;
}
int ModbusTcp_SetReadRequest(CapabilityType capabilityType, void* capability, byte unitId)
{
switch (capabilityType)
{
case Telemetry:
{
ModbusTelemetry* telemetry = (ModbusTelemetry*)capability;
UINT16 modbusAddress = 0;
if (!ModbusConnectionHelper_GetFunctionCode(telemetry->StartAddress, true, &(telemetry->ReadRequest.TcpRequest.Payload.FunctionCode), &modbusAddress))
{
LogError("Failed to get Modbus function code for telemetry \"%s\".", telemetry->Name);
return -1;
}
telemetry->ReadRequest.TcpRequest.MBAP.ProtocolID_Hi = 0x00;
telemetry->ReadRequest.TcpRequest.MBAP.ProtocolID_Lo = 0x00;
telemetry->ReadRequest.TcpRequest.MBAP.Length_Hi = 0x00;
telemetry->ReadRequest.TcpRequest.MBAP.Length_Lo = 0x06;
telemetry->ReadRequest.TcpRequest.MBAP.UnitID = unitId;
telemetry->ReadRequest.TcpRequest.Payload.StartAddr_Hi = (modbusAddress >> 8) & 0xff;
telemetry->ReadRequest.TcpRequest.Payload.StartAddr_Lo = modbusAddress & 0xff;
telemetry->ReadRequest.TcpRequest.Payload.ReadLen_Hi = (telemetry->Length >> 8) & 0xff;
telemetry->ReadRequest.TcpRequest.Payload.ReadLen_Lo = telemetry->Length & 0xff;
break;
}
case Property:
{
ModbusProperty* property = (ModbusProperty*)capability;
UINT16 modbusAddress = 0;
if (!ModbusConnectionHelper_GetFunctionCode(property->StartAddress, true, &(property->ReadRequest.TcpRequest.Payload.FunctionCode), &modbusAddress))
{
LogError("Failed to get Modbus function code for property \"%s\".", property->Name);
return -1;
}
property->ReadRequest.TcpRequest.MBAP.ProtocolID_Hi = 0x00;
property->ReadRequest.TcpRequest.MBAP.ProtocolID_Lo = 0x00;
property->ReadRequest.TcpRequest.MBAP.Length_Hi = 0x00;
property->ReadRequest.TcpRequest.MBAP.Length_Lo = 0x06;
property->ReadRequest.TcpRequest.MBAP.UnitID = unitId;
property->ReadRequest.TcpRequest.Payload.StartAddr_Hi = (modbusAddress >> 8) & 0xff;
property->ReadRequest.TcpRequest.Payload.StartAddr_Lo = modbusAddress & 0xff;
property->ReadRequest.TcpRequest.Payload.ReadLen_Hi = (property->Length >> 8) & 0xff;
property->ReadRequest.TcpRequest.Payload.ReadLen_Lo = property->Length & 0xff;
break;
}
default:
LogError("Modbus read if not supported for the capability.");
return -1;
}
return 0;
}
int ModbusTcp_SetWriteRequest(CapabilityType capabilityType, void* capability, char* valueStr)
{
UINT16 binaryData = 0;
switch (capabilityType)
{
case Command:
{
ModbusCommand* command = (ModbusCommand*)capability;
UINT16 modbusAddress = 0;
if (!ModbusConnectionHelper_GetFunctionCode(command->StartAddress, false, &(command->WriteRequest.TcpRequest.Payload.FunctionCode), &modbusAddress))
{
LogError("Failed to get Modbus function code for command \"%s\".", command->Name);
return -1;
}
if (!ModbusConnectionHelper_ConvertValueStrToUInt16(command->DataType, command->WriteRequest.TcpRequest.Payload.FunctionCode, valueStr, &binaryData))
{
LogError("Failed to convert data \"%s\" to byte array command \"%s\".", valueStr, command->Name);
return -1;
}
command->WriteRequest.TcpRequest.MBAP.ProtocolID_Hi = 0x00;
command->WriteRequest.TcpRequest.MBAP.ProtocolID_Lo = 0x00;
command->WriteRequest.TcpRequest.MBAP.Length_Hi = 0x00;
command->WriteRequest.TcpRequest.MBAP.Length_Lo = 0x06;
command->WriteRequest.TcpRequest.Payload.RegAddr_Hi = (modbusAddress >> 8) & 0xff;
command->WriteRequest.TcpRequest.Payload.RegAddr_Lo = modbusAddress & 0xff;
command->WriteRequest.TcpRequest.Payload.Value_Hi = (binaryData >> 8) & 0xff;
command->WriteRequest.TcpRequest.Payload.Value_Lo = binaryData & 0xff;
break;
}
case Property:
{
ModbusProperty* property = (ModbusProperty*)capability;
UINT16 modbusAddress = 0;
if (!ModbusConnectionHelper_GetFunctionCode(property->StartAddress, false, &(property->WriteRequest.TcpRequest.Payload.FunctionCode), &modbusAddress))
{
LogError("Failed to get Modbus function code for property \"%s\".", property->Name);
return -1;
}
if (!ModbusConnectionHelper_ConvertValueStrToUInt16(property->DataType, property->WriteRequest.TcpRequest.Payload.FunctionCode, valueStr, &binaryData))
{
LogError("Failed to convert data \"%s\" to byte array command \"%s\".", valueStr, property->Name);
return -1;
}
property->WriteRequest.TcpRequest.MBAP.ProtocolID_Hi = 0x00;
property->WriteRequest.TcpRequest.MBAP.ProtocolID_Lo = 0x00;
property->WriteRequest.TcpRequest.MBAP.Length_Hi = 0x00;
property->WriteRequest.TcpRequest.MBAP.Length_Lo = 0x06;
property->WriteRequest.TcpRequest.Payload.RegAddr_Hi = (modbusAddress >> 8) & 0xff;
property->WriteRequest.TcpRequest.Payload.RegAddr_Lo = modbusAddress & 0xff;
property->WriteRequest.TcpRequest.Payload.Value_Hi = (binaryData >> 8) & 0xff;
property->WriteRequest.TcpRequest.Payload.Value_Lo = binaryData & 0xff;
break;
}
default:
LogError("Modbus read if not supported for the capability.");
return -1;
}
return 0;
}
int ModbusTcp_SendRequest(SOCKET socket, byte *requestArr, DWORD arrLen)
{
if (INVALID_SOCKET == socket || 0 == socket || NULL == requestArr)
{
LogError("Failed to send request: connection handler and/or the request array is NUlL.");
return -1;
}
fd_set fds;
FD_ZERO(&fds);
socket_timeout.tv_sec = SOCKET_TIMEOUT_SEC;
socket_timeout.tv_usec = SOCKET_TIMEOUT_USEC;
// Check handler readiness
FD_SET(socket, &fds);
int socketReady = select(32, NULL, &fds, NULL, &socket_timeout);
if (0 == socketReady)
{
LogError("Socket not ready.");
return -1;
}
else if (SOCKET_ERROR == socketReady)
{
LogError("Failed to get socket readiness with error: %ld.", WSAGetLastError());
return -1;
}
int totalBytesSent = send(socket, (const char*)requestArr, arrLen, 0);
if (SOCKET_ERROR == totalBytesSent) {
//error setting serial port state
LogError("Failed to send request through coket with error: %ld.", WSAGetLastError());
}
return totalBytesSent;
}
int ModbusTcp_ReadResponse(SOCKET socket, byte *response, DWORD arrLen)
{
if (INVALID_SOCKET == socket || 0 == socket || NULL == response)
{
LogError("Failed to read response: connection handler and/or the request array is NUlL.");
return -1;
}
int bytesReceived = 0;
int totalBytesReceived = 0;
fd_set fds;
FD_ZERO(&fds);
FD_SET(socket, &fds);
// Check handler readiness
int socketReady = select(32, NULL, &fds, NULL, &socket_timeout);
if (0 == socketReady)
{
LogError("Socket not ready.");
return -1;
}
else if (SOCKET_ERROR == socketReady)
{
LogError("Failed to get socket readiness with error: %ld.", WSAGetLastError());
return -1;
}
bytesReceived = recv(socket, (char*)(response + totalBytesReceived), arrLen - totalBytesReceived, 0);
if (bytesReceived > 0)
{
totalBytesReceived += bytesReceived;
}
else if (SOCKET_ERROR == bytesReceived)
{
LogError("Failed to read from socket with error: %ld.", WSAGetLastError());
return -1;
}
return totalBytesReceived;
#include "ModbusTCPConnection.h"
// timeout interval for waiting for socket readiness
#define SOCKET_TIMEOUT_SEC 5
#define SOCKET_TIMEOUT_USEC 0
#ifdef WIN32
struct timeval socket_timeout;
#else
#define INFINITE 0xFFFFFFFF
#endif // WIN32
int ModbusTcp_GetHeaderSize(void) {
return TCP_HEADER_SIZE;
}
bool ModbusTcp_CloseDevice(SOCKET socket, LOCK_HANDLE hLock)
{
if (LOCK_OK == Lock(hLock))
{
LogError("Device communicate lock could not be acquired.");
return false;
}
shutdown(socket, 1);
int byteRcvd = 1;
BYTE buffer[MODBUS_RESPONSE_MAX_LENGTH];
while (0 < byteRcvd)
{
byteRcvd = recv(socket, (char*)buffer, MODBUS_RESPONSE_MAX_LENGTH, 0);
}
#ifdef WIN32
closesocket(socket);
WSACleanup();
#else
close(socket);
#endif
Unlock(hLock);
LogInfo("Socket Closed.");
return true;
}
int ModbusTcp_SetReadRequest(CapabilityType capabilityType, void* capability, byte unitId)
{
switch (capabilityType)
{
case Telemetry:
{
ModbusTelemetry* telemetry = (ModbusTelemetry*)capability;
UINT16 modbusAddress = 0;
if (!ModbusConnectionHelper_GetFunctionCode(telemetry->StartAddress, true, &(telemetry->ReadRequest.TcpRequest.Payload.FunctionCode), &modbusAddress))
{
LogError("Failed to get Modbus function code for telemetry \"%s\".", telemetry->Name);
return -1;
}
telemetry->ReadRequest.TcpRequest.MBAP.ProtocolID_Hi = 0x00;
telemetry->ReadRequest.TcpRequest.MBAP.ProtocolID_Lo = 0x00;
telemetry->ReadRequest.TcpRequest.MBAP.Length_Hi = 0x00;
telemetry->ReadRequest.TcpRequest.MBAP.Length_Lo = 0x06;
telemetry->ReadRequest.TcpRequest.MBAP.UnitID = unitId;
telemetry->ReadRequest.TcpRequest.Payload.StartAddr_Hi = (modbusAddress >> 8) & 0xff;
telemetry->ReadRequest.TcpRequest.Payload.StartAddr_Lo = modbusAddress & 0xff;
telemetry->ReadRequest.TcpRequest.Payload.ReadLen_Hi = (telemetry->Length >> 8) & 0xff;
telemetry->ReadRequest.TcpRequest.Payload.ReadLen_Lo = telemetry->Length & 0xff;
break;
}
case Property:
{
ModbusProperty* property = (ModbusProperty*)capability;
UINT16 modbusAddress = 0;
if (!ModbusConnectionHelper_GetFunctionCode(property->StartAddress, true, &(property->ReadRequest.TcpRequest.Payload.FunctionCode), &modbusAddress))
{
LogError("Failed to get Modbus function code for property \"%s\".", property->Name);
return -1;
}
property->ReadRequest.TcpRequest.MBAP.ProtocolID_Hi = 0x00;
property->ReadRequest.TcpRequest.MBAP.ProtocolID_Lo = 0x00;
property->ReadRequest.TcpRequest.MBAP.Length_Hi = 0x00;
property->ReadRequest.TcpRequest.MBAP.Length_Lo = 0x06;
property->ReadRequest.TcpRequest.MBAP.UnitID = unitId;
property->ReadRequest.TcpRequest.Payload.StartAddr_Hi = (modbusAddress >> 8) & 0xff;
property->ReadRequest.TcpRequest.Payload.StartAddr_Lo = modbusAddress & 0xff;
property->ReadRequest.TcpRequest.Payload.ReadLen_Hi = (property->Length >> 8) & 0xff;
property->ReadRequest.TcpRequest.Payload.ReadLen_Lo = property->Length & 0xff;
break;
}
default:
LogError("Modbus read if not supported for the capability.");
return -1;
}
return 0;
}
int ModbusTcp_SetWriteRequest(CapabilityType capabilityType, void* capability, char* valueStr)
{
UINT16 binaryData = 0;
switch (capabilityType)
{
case Command:
{
ModbusCommand* command = (ModbusCommand*)capability;
UINT16 modbusAddress = 0;
if (!ModbusConnectionHelper_GetFunctionCode(command->StartAddress, false, &(command->WriteRequest.TcpRequest.Payload.FunctionCode), &modbusAddress))
{
LogError("Failed to get Modbus function code for command \"%s\".", command->Name);
return -1;
}
if (!ModbusConnectionHelper_ConvertValueStrToUInt16(command->DataType, command->WriteRequest.TcpRequest.Payload.FunctionCode, valueStr, &binaryData))
{
LogError("Failed to convert data \"%s\" to byte array command \"%s\".", valueStr, command->Name);
return -1;
}
command->WriteRequest.TcpRequest.MBAP.ProtocolID_Hi = 0x00;
command->WriteRequest.TcpRequest.MBAP.ProtocolID_Lo = 0x00;
command->WriteRequest.TcpRequest.MBAP.Length_Hi = 0x00;
command->WriteRequest.TcpRequest.MBAP.Length_Lo = 0x06;
command->WriteRequest.TcpRequest.Payload.RegAddr_Hi = (modbusAddress >> 8) & 0xff;
command->WriteRequest.TcpRequest.Payload.RegAddr_Lo = modbusAddress & 0xff;
command->WriteRequest.TcpRequest.Payload.Value_Hi = (binaryData >> 8) & 0xff;
command->WriteRequest.TcpRequest.Payload.Value_Lo = binaryData & 0xff;
break;
}
case Property:
{
ModbusProperty* property = (ModbusProperty*)capability;
UINT16 modbusAddress = 0;
if (!ModbusConnectionHelper_GetFunctionCode(property->StartAddress, false, &(property->WriteRequest.TcpRequest.Payload.FunctionCode), &modbusAddress))
{
LogError("Failed to get Modbus function code for property \"%s\".", property->Name);
return -1;
}
if (!ModbusConnectionHelper_ConvertValueStrToUInt16(property->DataType, property->WriteRequest.TcpRequest.Payload.FunctionCode, valueStr, &binaryData))
{
LogError("Failed to convert data \"%s\" to byte array command \"%s\".", valueStr, property->Name);
return -1;
}
property->WriteRequest.TcpRequest.MBAP.ProtocolID_Hi = 0x00;
property->WriteRequest.TcpRequest.MBAP.ProtocolID_Lo = 0x00;
property->WriteRequest.TcpRequest.MBAP.Length_Hi = 0x00;
property->WriteRequest.TcpRequest.MBAP.Length_Lo = 0x06;
property->WriteRequest.TcpRequest.Payload.RegAddr_Hi = (modbusAddress >> 8) & 0xff;
property->WriteRequest.TcpRequest.Payload.RegAddr_Lo = modbusAddress & 0xff;
property->WriteRequest.TcpRequest.Payload.Value_Hi = (binaryData >> 8) & 0xff;
property->WriteRequest.TcpRequest.Payload.Value_Lo = binaryData & 0xff;
break;
}
default:
LogError("Modbus read if not supported for the capability.");
return -1;
}
return 0;
}
int ModbusTcp_SendRequest(SOCKET socket, byte *requestArr, DWORD arrLen)
{
if (INVALID_SOCKET == socket || 0 == socket || NULL == requestArr)
{
LogError("Failed to send request: connection handler and/or the request array is null.");
return -1;
}
#ifdef WIN32
fd_set fds;
FD_ZERO(&fds);
socket_timeout.tv_sec = SOCKET_TIMEOUT_SEC;
socket_timeout.tv_usec = SOCKET_TIMEOUT_USEC;
// Check handler readiness
FD_SET(socket, &fds);
int socketReady = select(32, NULL, &fds, NULL, &socket_timeout);
if (0 == socketReady)
{
LogError("Socket not ready.");
return -1;
}
else if (SOCKET_ERROR == socketReady)
{
LogError("Failed to get socket readiness with error: %ld.", WSAGetLastError());
return -1;
}
#endif
int totalBytesSent = send(socket, (const char*)requestArr, arrLen, 0);
if (SOCKET_ERROR == totalBytesSent) {
//error setting serial port state
#ifdef WIN32
LogError("Failed to send request through socket with error: %ld.", WSAGetLastError());
#else
LogError("Failed to send request through socket.");
#endif
}
return totalBytesSent;
}
int ModbusTcp_ReadResponse(SOCKET socket, byte *response, DWORD arrLen)
{
if (INVALID_SOCKET == socket || 0 == socket || NULL == response)
{
LogError("Failed to read response: connection handler and/or the request array is null.");
return -1;
}
int bytesReceived = 0;
int totalBytesReceived = 0;
#ifdef WIN32
fd_set fds;
FD_ZERO(&fds);
FD_SET(socket, &fds);
// Check handler readiness
int socketReady = select(32, NULL, &fds, NULL, &socket_timeout);
if (0 == socketReady)
{
LogError("Socket not ready.");
return -1;
}
else if (SOCKET_ERROR == socketReady)
{
LogError("Failed to get socket readiness with error: %ld.", WSAGetLastError());
return -1;
}
#endif
bytesReceived = recv(socket, (char*)(response + totalBytesReceived), arrLen - totalBytesReceived, 0);
if (bytesReceived > 0)
{
totalBytesReceived += bytesReceived;
}
else if (SOCKET_ERROR == bytesReceived)
{
#ifdef WIN32
LogError("Failed to read from socket with error: %ld.", WSAGetLastError());
#else
LogError("Failed to read from socket.");
#endif
return -1;
}
return totalBytesReceived;
}

Просмотреть файл

@ -1,23 +1,35 @@
#pragma once
#ifdef __cplusplus
extern "C"
{
#endif
#include "azure_c_shared_utility/xlogging.h"
#include "../ModbusCapability.h"
#include "ModbusConnectionHelper.h"
#define TCP_HEADER_SIZE 7 // TransactionID (2 bytes) + ProtocolID (2 bytes) + Length (2 bytes) + UnitID (1 byte)
int ModbusTcp_GetHeaderSize(void);
bool ModbusTcp_CloseDevice(SOCKET hDevice, HANDLE lock);
int ModbusTcp_SetReadRequest(CapabilityType capabilityType, void* capability, byte unitId);
int ModbusTcp_SetWriteRequest(CapabilityType capabilityType, void* capability, char* valueStr);
int ModbusTcp_SendRequest(SOCKET handler, byte *requestArr, DWORD arrLen);
int ModbusTcp_ReadResponse(SOCKET handler, byte *response, DWORD arrLen);
#ifdef __cplusplus
}
#pragma once
#ifdef __cplusplus
extern "C"
{
#endif
#include "azure_c_shared_utility/xlogging.h"
#include "../ModbusCapability.h"
#include "ModbusConnectionHelper.h"
#ifndef WIN32
#include <sys/termios.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#define SOCKET int
#define SOCKET_ERROR (-1)
#define INVALID_SOCKET (~0)
#endif
#define TCP_HEADER_SIZE 7 // TransactionID (2 bytes) + ProtocolID (2 bytes) + Length (2 bytes) + UnitID (1 byte)
int ModbusTcp_GetHeaderSize(void);
bool ModbusTcp_CloseDevice(SOCKET hDevice, LOCK_HANDLE lock);
int ModbusTcp_SetReadRequest(CapabilityType capabilityType, void* capability, byte unitId);
int ModbusTcp_SetWriteRequest(CapabilityType capabilityType, void* capability, char* valueStr);
int ModbusTcp_SendRequest(SOCKET handler, byte *requestArr, DWORD arrLen);
int ModbusTcp_ReadResponse(SOCKET handler, byte *response, DWORD arrLen);
#ifdef __cplusplus
}
#endif

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,80 +1,118 @@
#pragma once
#ifdef __cplusplus
extern "C"
{
#endif
#include <pnpbridge.h>
#include "azure_c_shared_utility/threadapi.h"
#include "azure_c_shared_utility/singlylinkedlist.h"
#include "azure_c_shared_utility/lock.h"
#include <Windows.h>
#include <cfgmgr32.h>
#include <ctype.h>
#include "ModbusEnum.h"
typedef struct _MODBUS_RTU_CONFIG
{
char* Port;
DWORD BaudRate;
BYTE DataBits;
BYTE StopBits;
BYTE Parity;
} MODBUS_RTU_CONFIG;
typedef struct _MODBUS_TCP_CONFIG
{
char* Host;
UINT16 Port;
} MODBUS_TCP_CONFIG;
typedef union _MODBUS_CONNECTION_CONFIG
{
MODBUS_RTU_CONFIG RtuConfig;
MODBUS_TCP_CONFIG TcpConfig;
} MODBUS_CONNECTION_CONFIG;
typedef struct ModbusDeviceConfig
{
BYTE UnitId;
MODBUS_CONNECTION_TYPE ConnectionType;
MODBUS_CONNECTION_CONFIG ConnectionConfig;
} ModbusDeviceConfig, *PModbusDeviceConfig;
typedef struct ModbusInterfaceConfig
{
const char* Id;
int Index;
SINGLYLINKEDLIST_HANDLE Events;
SINGLYLINKEDLIST_HANDLE Properties;
SINGLYLINKEDLIST_HANDLE Commands;
} ModbusInterfaceConfig;
typedef struct _MODBUS_DEVICE_CONTEXT {
HANDLE hDevice;
HANDLE hConnectionLock;
PNPADAPTER_INTERFACE_HANDLE pnpAdapterInterface;
THREAD_HANDLE ModbusDeviceWorker;
PNPBRIDGE_NOTIFY_CHANGE ModbusDeviceChangeCallback;
// list of interface definitions on this modbus device
PModbusDeviceConfig DeviceConfig;
SINGLYLINKEDLIST_HANDLE InterfaceDefinitions;
THREAD_HANDLE* PollingTasks;
} MODBUS_DEVICE_CONTEXT, *PMODBUS_DEVICE_CONTEXT;
int ModbusPnp_GetListCount(SINGLYLINKEDLIST_HANDLE list);
// TODO: Fix this missing reference
#ifndef AZURE_UNREFERENCED_PARAMETER
#define AZURE_UNREFERENCED_PARAMETER(param) (void)(param)
#endif
#ifdef __cplusplus
}
#endif
#pragma once
#ifdef __cplusplus
extern "C"
{
#endif
#include <pnpbridge.h>
#include "azure_c_shared_utility/threadapi.h"
#include "azure_c_shared_utility/singlylinkedlist.h"
#include "azure_c_shared_utility/lock.h"
#include "azure_c_shared_utility/condition.h"
#include <ctype.h>
#ifdef WIN32
#include <Windows.h>
#include <cfgmgr32.h>
#define INVALID_FILE INVALID_HANDLE_VALUE
#else
typedef unsigned int DWORD;
typedef uint8_t BYTE;
typedef int HANDLE;
typedef int SOCKET;
typedef short USHORT;
typedef uint16_t UINT16;
typedef uint32_t UINT32;
typedef uint64_t UINT64;
#define INVALID_FILE -1
#define INVALID_HANDLE_VALUE -1
#define iscsym(c) (isalnum(c) || ((c) == '_'))
#include <errno.h>
#include <string.h>
#include <sys/termios.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#define ONESTOPBIT 0
#define ONE5STOPBITS 1
#define TWOSTOPBITS 2
//parity
#define NOPARITY 0
#define ODDPARITY 1
#define EVENPARITY 2
#define MARKPARITY 3
#define SPACEPARITY 4
#define UNREFERENCED_PARAMETER AZURE_UNREFERENCED_PARAMETER
#endif
#include "ModbusEnum.h"
typedef struct _MODBUS_RTU_CONFIG
{
char* Port;
DWORD BaudRate;
BYTE DataBits;
BYTE StopBits;
BYTE Parity;
} MODBUS_RTU_CONFIG;
typedef struct _MODBUS_TCP_CONFIG
{
char* Host;
UINT16 Port;
} MODBUS_TCP_CONFIG;
typedef union _MODBUS_CONNECTION_CONFIG
{
MODBUS_RTU_CONFIG RtuConfig;
MODBUS_TCP_CONFIG TcpConfig;
} MODBUS_CONNECTION_CONFIG;
typedef struct ModbusDeviceConfig
{
BYTE UnitId;
MODBUS_CONNECTION_TYPE ConnectionType;
MODBUS_CONNECTION_CONFIG ConnectionConfig;
} ModbusDeviceConfig, *PModbusDeviceConfig;
typedef struct ModbusInterfaceConfig
{
const char* Id;
int Index;
SINGLYLINKEDLIST_HANDLE Events;
SINGLYLINKEDLIST_HANDLE Properties;
SINGLYLINKEDLIST_HANDLE Commands;
} ModbusInterfaceConfig;
typedef struct _MODBUS_DEVICE_CONTEXT {
HANDLE hDevice;
LOCK_HANDLE hConnectionLock;
PNPADAPTER_INTERFACE_HANDLE pnpAdapterInterface;
THREAD_HANDLE ModbusDeviceWorker;
PNPBRIDGE_NOTIFY_CHANGE ModbusDeviceChangeCallback;
// list of interface definitions on this modbus device
PModbusDeviceConfig DeviceConfig;
SINGLYLINKEDLIST_HANDLE InterfaceDefinitions;
THREAD_HANDLE* PollingTasks;
} MODBUS_DEVICE_CONTEXT, *PMODBUS_DEVICE_CONTEXT;
int ModbusPnp_GetListCount(SINGLYLINKEDLIST_HANDLE list);
// TODO: Fix this missing reference
#ifndef AZURE_UNREFERENCED_PARAMETER
#define AZURE_UNREFERENCED_PARAMETER(param) (void)(param)
#endif
#ifdef __cplusplus
}
#endif

Просмотреть файл

@ -1,50 +1,51 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
// Discovery and Pnp Adapter headers
#include <pnpbridge.h>
extern DISCOVERY_ADAPTER SerialPnpDiscovery;
extern DISCOVERY_ADAPTER ModbusPnpDeviceDiscovery;
extern PNP_ADAPTER SerialPnpInterface;
extern PNP_ADAPTER ModbusPnpInterface;
#ifdef WIN32
extern DISCOVERY_ADAPTER CameraPnpAdapter;
extern DISCOVERY_ADAPTER CoreDeviceDiscovery;
PDISCOVERY_ADAPTER DISCOVERY_ADAPTER_MANIFEST[] = {
&CameraPnpAdapter,
&SerialPnpDiscovery,
&CoreDeviceDiscovery,
&ModbusPnpDeviceDiscovery
};
extern PNP_ADAPTER CameraPnpInterface;
extern PNP_ADAPTER CoreDeviceHealth;
PPNP_ADAPTER PNP_ADAPTER_MANIFEST[] = {
&CameraPnpInterface,
&SerialPnpInterface,
&CoreDeviceHealth,
&ModbusPnpInterface
};
#else //WIN32
PDISCOVERY_ADAPTER DISCOVERY_ADAPTER_MANIFEST[] = {
&SerialPnpDiscovery
};
//&ModbusPnpInterface
PPNP_ADAPTER PNP_ADAPTER_MANIFEST[] = {
&SerialPnpInterface
};
// &ModbusPnpInterface
#endif
const int DiscoveryAdapterCount = sizeof(DISCOVERY_ADAPTER_MANIFEST) / sizeof(PDISCOVERY_ADAPTER);
const int PnpAdapterCount = sizeof(PNP_ADAPTER_MANIFEST) / sizeof(PPNP_ADAPTER);
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
// Discovery and Pnp Adapter headers
#include <pnpbridge.h>
extern DISCOVERY_ADAPTER SerialPnpDiscovery;
extern DISCOVERY_ADAPTER ModbusPnpDeviceDiscovery;
extern PNP_ADAPTER SerialPnpInterface;
extern PNP_ADAPTER ModbusPnpInterface;
#ifdef WIN32
extern DISCOVERY_ADAPTER CameraPnpAdapter;
extern DISCOVERY_ADAPTER CoreDeviceDiscovery;
PDISCOVERY_ADAPTER DISCOVERY_ADAPTER_MANIFEST[] = {
&CameraPnpAdapter,
&SerialPnpDiscovery,
&CoreDeviceDiscovery,
&ModbusPnpDeviceDiscovery
};
extern PNP_ADAPTER CameraPnpInterface;
extern PNP_ADAPTER CoreDeviceHealth;
PPNP_ADAPTER PNP_ADAPTER_MANIFEST[] = {
&CameraPnpInterface,
&SerialPnpInterface,
&CoreDeviceHealth,
&ModbusPnpInterface
};
#else //WIN32
PDISCOVERY_ADAPTER DISCOVERY_ADAPTER_MANIFEST[] = {
&SerialPnpDiscovery,
&ModbusPnpDeviceDiscovery
};
PPNP_ADAPTER PNP_ADAPTER_MANIFEST[] = {
&SerialPnpInterface,
&ModbusPnpInterface
};
#endif
const int DiscoveryAdapterCount = sizeof(DISCOVERY_ADAPTER_MANIFEST) / sizeof(PDISCOVERY_ADAPTER);
const int PnpAdapterCount = sizeof(PNP_ADAPTER_MANIFEST) / sizeof(PPNP_ADAPTER);

Просмотреть файл

@ -74,12 +74,12 @@ set(pnp_bridge_common_libs
pnpbridge_adapters
digitaltwin_client
pnpbridge_serial
pnpbridge_modbus
)
if(WIN32)
set(pnp_bridge_common_libs
${pnp_bridge_common_libs}
pnpbridge_modbus
pnpbridge_camera
pnpbridge_coredevicehealth
)