Extending modbus adapter to support linux
This commit is contained in:
Родитель
cef6def7f1
Коммит
0671c21000
|
@ -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
|
||||
)
|
||||
|
|
Загрузка…
Ссылка в новой задаче