terryyin / lizard

A simple code complexity analyser without caring about the C/C++ header files or Java imports, supports most of the popular languages.
Other
1.87k stars 252 forks source link

McCabe numbers for C code are too high when compared with other commercial tools #233

Open geoland1st opened 6 years ago

geoland1st commented 6 years ago

I am looking to use this tool to measure McCabe Cyclomatic Complexity on the C code in our project. For example, when I run Understand for C or Crystal Flow on a particularly complicated function they both give the CCN as 101. Lizard gives a CCN of 140. If I add the -Emccabe option, then Lizard gives a CCN of 136. I see this inflation in other, less complex functions as well. I love how easy this tool is to use, but I don't feel I can trust the numbers it reports.

terryyin commented 6 years ago

Hi @geoland1st, thanks for using Lizard. Lizard has been serving many users for many years. I guess it's result is useful:-) Could you give me an example (obfuscate it if it's sensitive) so that I can investigate?

Right now, my guess is it's because of the pre-processor directives. Lizard will count #if as a condition.

geoland1st commented 6 years ago

Hi Terry,

Thanks for your quick response. I will send you the code in my morning (maybe your evening). I expect you are right about the preprocessor conditionals. Any way to get around it? I wonder what those other tools do. I do believe you have made a useful tool. I discovered it because a large company I work with uses it.

George Anderson

On Jul 19, 2018, at 9:02 PM, Terry Yin notifications@github.com<mailto:notifications@github.com> wrote:

Hi @geoland1sthttps://github.com/geoland1st, thanks for using Lizard. Lizard has been serving many users for many years. I guess it's result is useful:-) Could you give me an example (obfuscate it if it's sensitive) so that I can investigate?

Right now, my guess is it's because of the pre-processor directives. Lizard will count #if as a condition.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHubhttps://github.com/terryyin/lizard/issues/233#issuecomment-406482837, or mute the threadhttps://github.com/notifications/unsubscribe-auth/AniQx8bBPFy-GsK3PxtkLTwNwU01h651ks5uIVZNgaJpZM4VXSW7.

terryyin commented 6 years ago

George, thanks and please send.

If it’s because of the preprocessor, -Ecpre might improve the result a bit. It will choose the first branch in #if only. But personal opinion, I think #if should be counted as CCN.

On 20 Jul 2018, at 12:49 PM, geoland1st notifications@github.com wrote:

Hi Terry,

Thanks for your quick response. I will send you the code in my morning (maybe your evening). I expect you are right about the preprocessor conditionals. Any way to get around it? I wonder what those other tools do. I do believe you have made a useful tool. I discovered it because a large company I work with uses it.

George Anderson

On Jul 19, 2018, at 9:02 PM, Terry Yin notifications@github.com<mailto:notifications@github.com> wrote:

Hi @geoland1sthttps://github.com/geoland1st, thanks for using Lizard. Lizard has been serving many users for many years. I guess it's result is useful:-) Could you give me an example (obfuscate it if it's sensitive) so that I can investigate?

Right now, my guess is it's because of the pre-processor directives. Lizard will count #if as a condition.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHubhttps://github.com/terryyin/lizard/issues/233#issuecomment-406482837, or mute the threadhttps://github.com/notifications/unsubscribe-auth/AniQx8bBPFy-GsK3PxtkLTwNwU01h651ks5uIVZNgaJpZM4VXSW7. — You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/terryyin/lizard/issues/233#issuecomment-406488174, or mute the thread https://github.com/notifications/unsubscribe-auth/AAwJYlTrQE8mjJYX_8CWWWn70rj7d7lvks5uIWFjgaJpZM4VXSW7.

geoland1st commented 6 years ago

Okay, code is attached. The ‘-Ecpre’ dropped the CCN to 119, which is closer, but not matching the other tools score of 101.

terryyin commented 6 years ago

Hi George,

Thanks for sharing the code. Yes, just as I mentioned previously, it’s because of the preprocessor directives.

I started Lizard when I was working in Nokia in my spare time, to replace a commercial tool we were using. And it was called HFCCA — Header Free Cyclomatic Complexity Analyzer. It doesn’t depend the header file, and just treat header files as source code. Therefore it cannot parse preprocessor directive properly. The good side of this tradeoff is, Lizard shows how complicated the.code looks, from the human reader’s perspective, not the compiler’s perspective after preprocessing.

Terry

On 21 Jul 2018, at 7:06 AM, geoland1st notifications@github.com wrote:

Okay, code is attached. The ‘-Ecpre’ dropped the CCN to 119, which is closer, but not matching the other tools score of 101.

  • George

From: Terry Yin notifications@github.com Sent: Thursday, July 19, 2018 23:05 To: terryyin/lizard lizard@noreply.github.com Cc: Anderson, George ganderson@spectrumcontrols.com; Mention mention@noreply.github.com Subject: Re: [terryyin/lizard] McCabe numbers for C code are too high when compared with other commercial tools (#233)

George, thanks and please send.

If it’s because of the preprocessor, -Ecpre might improve the result a bit. It will choose the first branch in #if only. But personal opinion, I think #if should be counted as CCN.

On 20 Jul 2018, at 12:49 PM, geoland1st notifications@github.com<mailto:notifications@github.com> wrote:

Hi Terry,

Thanks for your quick response. I will send you the code in my morning (maybe your evening). I expect you are right about the preprocessor conditionals. Any way to get around it? I wonder what those other tools do. I do believe you have made a useful tool. I discovered it because a large company I work with uses it.

George Anderson

On Jul 19, 2018, at 9:02 PM, Terry Yin notifications@github.com<mailto:notifications@github.com<mailto:notifications@github.com%3cmailto:notifications@github.com>> wrote:

Hi @geoland1sthttps://github.com/geoland1st, thanks for using Lizard. Lizard has been serving many users for many years. I guess it's result is useful:-) Could you give me an example (obfuscate it if it's sensitive) so that I can investigate?

Right now, my guess is it's because of the pre-processor directives. Lizard will count #if as a condition.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHubhttps://github.com/terryyin/lizard/issues/233#issuecomment-406482837, or mute the threadhttps://github.com/notifications/unsubscribe-auth/AniQx8bBPFy-GsK3PxtkLTwNwU01h651ks5uIVZNgaJpZM4VXSW7. — You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/terryyin/lizard/issues/233#issuecomment-406488174, or mute the thread https://github.com/notifications/unsubscribe-auth/AAwJYlTrQE8mjJYX_8CWWWn70rj7d7lvks5uIWFjgaJpZM4VXSW7.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHubhttps://github.com/terryyin/lizard/issues/233#issuecomment-406498178, or mute the threadhttps://github.com/notifications/unsubscribe-auth/AniQxyOnnAPh1ljFgil96zN2FPbZUetHks5uIXMigaJpZM4VXSW7.

/***

  • $Archive: M:\PVCS\pvcs-ver65\archives\1756 HART Brand Label\appsrc\hartapp.c-arc $
  • $Workfile: hartapp.c $
  • $Revision: 1.0 $
  • $Author: gbrown $
  • $Date: 29 Jan 2007 10:12:48 $
  • $Modtime: 24 Jan 2007 15:37:58 $
  • Copyright (c) 2002-2007 Spectrum Controls, Inc.
  • P.O. Box 5533
  • Bellevue, WA 98006
  • Phone: (425) 746-9481
  • CONFIDENTIAL INFORMATION:
  • Spectrum Controls Inc, has proprietary rights to the information
  • contained hereon. This information is to remain the property of
  • Spectrum Controls Inc, and may be used only in conformance with
  • written instructions issued by and from Spectrum Controls Inc.
  • This information is issued in strict confidence.
  • It may not be reproduced in any way without prior written
  • permission from Spectrum Controls Inc, and may be changed and/or
  • recalled without notice.
  • Processor: Philips 8051XA G3 (P51XAG30KBA)
  • Compiler: Tasking C V4.0 r1
  • Products: 1756 Analog Modules with HART
  • Abstract:
  • History:
  • $Log: M:\PVCS\pvcs-ver65\archives\1756 HART Brand Label\appsrc\hartapp.c-arc $
  • Rev 1.0 29 Jan 2007 10:12:48 gbrown
  • Initial revision.
  • ***/

    include

include "..\sharedsrc\basetype.h"

include "APPConnectionManager.h"

include "fpmath.h"

include "hartuart.h" / HART LLC definition /

include "hartapp.h" / HART App definition /

include "timers.h"

include "scales.h"

include "HartPassThru.h"

extern DWORD raw_adc_data[]; extern NEAR WORD dac_data_hart[];

define HART_BITE_TIMEOUT MS_3000

define BiteMode (BITE_JUMPER == 0)

// Polling address 0 is default. 1..15 disable device from driving its analog output.

define N_ADDR_BETWEEN_0_POLL 2 // 0-1-2,0-3-4,0-5-6,0-7-8,etc.

static BYTE addr_group_attempts[HA_MAX_CHANNEL];

ifdef INCREASE_MAX_POLL_ADDR_TO_63_ONTIME_1676

define START_LOW_POLL_ADDRESS 1

define MAX_LOW_POLL_ADDRESS 15

define START_HI_POLL_ADDRESS 16

define MAX_HI_POLL_ADDRESS 63

static BYTE LowPollAddress[HA_MAX_CHANNEL]; // 1-15 static BYTE HiPollAddress[HA_MAX_CHANNEL]; // 16-63 static BYTE LowPollDone[HA_MAX_CHANNEL]; // Flag indicates last poll done was the low poll, time for hi poll

endif

define PtSetScanCnt() \

{ \ if (mod_cfg_blk.module_cfg_bits & 0x8000) \ ha_scan_counter = 1; \ else if (!(mod_cfg_blk.module_cfg_bits & 0x4000)) \ ha_scan_counter = 2; \ }

define PtDecScanCount() \

{ \ if (ha_scan_counter > 0) \ ha_scan_counter--; \ }

define PtPriorityMode() (mod_cfg_blk.module_cfg_bits & 0x8000)

define PtTrigger() \

((((mod_cfg_blk.module_cfg_bits & 0x8000) || \ !(mod_cfg_blk.module_cfg_bits & 0x4000)) && (ha_scan_counter == 0)) || \ (ha_full_scan_mask == 0))

// The Unconnected message handler: HA_ProcessUnconnectedMsg() // is called via the timer 1 interrupt service routine. // The HART application state machine is in the main thread. // Use the following macros to prevent contention between these two threads. BOOLEAN hart_ucm_enabled = 1;

define HartUcmDisable() \

{ \ ea = EA; / Save global interrupt enable. / \ EA = 0; / Disable all interrupts. / \ hart_ucm_enabled = 0; \ EA = ea; / Reenable all other interrutps. / \ }

define HartUcmEnable() \

{ \ ea = EA; / Save global interrupt enable. / \ EA = 0; / Disable all interrupts. / \ hart_ucm_enabled = 1; \ EA = ea; / Reenable all other interrutps. / \ }

/ local functions / _inline RESPONSE_CATEGORY CheckCommStatusBytes(BYTE channel, BYTE pData, BYTE CommandNumber); _inline void BypassVariableScan(BYTE channel); _inline void CommFailure(void); _inline BYTE FindNextChannel(BYTE current_channel); _inline void revmemcpy(BYTE dst, BYTE src, WORD size); _inline void ScheduleChannelUpdate(BYTE channel); void UpdateChannelStatus(void); _inline void UpdatePassthru(void); _inline void UpdateDeviceInformation(BYTE channel); void ClearDeviceInfo (BYTE channel); void ManageChannels(BYTE recovering); RESPONSE_CATEGORY ParseResponse(BYTE channel, BYTE ptBuffer); RESPONSE_CATEGORY ParseResponse_0(BYTE channel, BYTE *ptBuffer);

ifdef FIX_CMD_2_PARSE_NOT_BEING_CALLED_ONTIME_2135

RESPONSE_CATEGORY ParseResponse_2(BYTE channel, BYTE *ptBuffer);

endif

RESPONSE_CATEGORY ParseResponse_3(BYTE channel, BYTE ptBuffer); RESPONSE_CATEGORY ParseResponse_6(BYTE channel, BYTE ptBuffer); RESPONSE_CATEGORY ParseResponse_9(BYTE channel, BYTE ptBuffer); RESPONSE_CATEGORY ParseResponse_12(BYTE channel, BYTE ptBuffer); RESPONSE_CATEGORY ParseResponse_13(BYTE channel, BYTE ptBuffer); RESPONSE_CATEGORY ParseResponse_14(BYTE channel, BYTE ptBuffer); RESPONSE_CATEGORY ParseResponse_15(BYTE channel, BYTE ptBuffer); RESPONSE_CATEGORY ParseResponse_16(BYTE channel, BYTE ptBuffer); RESPONSE_CATEGORY ParseResponse_38(BYTE channel, BYTE ptBuffer); RESPONSE_CATEGORY ParseResponse_48(BYTE channel, BYTE ptBuffer); RESPONSE_CATEGORY ParseResponse_50(BYTE channel, BYTE ptBuffer); RESPONSE_CATEGORY ParseResponse_59(BYTE channel, BYTE ptBuffer); RESPONSE_CATEGORY ParseResponse_109 (BYTE channel, BYTE ptBuffer); void ResetHartData(BYTE channel); BYTE SendHartCommand(BYTE channel, BYTE command_number, BYTE global_event); BYTE StripResponseHeader(BYTE rawBuffer, WORD ptr_header_size); void UnpackAscii(char pUnpacked, int nUnpackedByteCount, BYTE pPacked);

/* * HART Application State Machine State Functions / void HaSm_Init(BYTE this_channel, BYTE init); void HaSmWaitToSwitchChannel(BYTE this_channel, BYTE init); void HaSmWaitToConfigure(BYTE this_channel, BYTE init); void HaSmConnecting(BYTE this_channel, BYTE init); void HaSmSampling(BYTE this_channel, BYTE init); void HaSmServicePassthru(BYTE this_channel, BYTE init); void HaSmGetDeviceInformation(BYTE this_channel, BYTE init); void HaSmServiceGlobalEvent(BYTE this_channel, BYTE init);

ifdef IMPLEMENT_SLOT_CODES

void HaSmSamplingDeviceVar(BYTE this_channel, BYTE init); void ResetHartDeviceData(BYTE channel);

endif // #ifdef IMPLEMENT_SLOT_CODES

/ Per AB's Request Unblock all restricted commands for the "Logix" & "Connects" Passthrough interface / / to prevent conflicts with the DTM sending these Cmd's. / / These commands will still be blocked for the Ladder Pass-through Interface. / / Ladder Pass-through restricted HART commands: / / CMD# Description / / --- ------------- / / 59 HA_WRITE_NUMBER_RESP_PREAMBLE, / / 107 HA_WRITE_BUST_MODE_TVS, / / 108 HA_WRITE_BURST_MODE_CMD_NUMBER, / / 109 HA_BURST_MODE_CONTROL, / CONST BYTE ha_restricted_hart_commands[] = { 0x00, / CMD 00 through 07 / 0x00, / CMD 08 through 15 / 0x00, / CMD 16 through 23 / 0x00, / CMD 24 through 31 / 0x00, / CMD 32 through 39 / 0x00, / CMD 40 through 47 / 0x00, / CMD 48 through 55 / 0x08, / CMD 56 through 63 / 0x00, / CMD 64 through 71 / 0x00, / CMD 72 through 79 / 0x00, / CMD 80 through 87 / 0x00, / CMD 88 through 95 / 0x00, / CMD 96 through 103 / 0x38, / CMD 104 through 111 / 0x00, / CMD 112 through 119 / 0x00, / CMD 120 through 127 / 0x00, / CMD 128 through 135 / 0x00, / CMD 136 through 143 / 0x00, / CMD 144 through 151 / 0x00, / CMD 152 through 159 / 0x00, / CMD 160 through 167 / 0x00, / CMD 168 through 175 / 0x00, / CMD 176 through 183 / 0x00, / CMD 184 through 191 / 0x00, / CMD 192 through 199 / 0x00, / CMD 200 through 207 / 0x00, / CMD 208 through 215 / 0x00, / CMD 216 through 223 / 0x00, / CMD 224 through 231 / 0x00, / CMD 232 through 239 / 0x00, / CMD 240 through 247 / 0x00 / CMD 248 through 255 / };

/ Byte Masks to AND with: For determining bit set or reset (bitmasks[] from hwbase.c) / / to OR with: For setting a bit. /

/ Byte Masks to AND with: For clearing a bit / CONST BYTE bitmasks_clear[] = { 0xfe, / 1111 1110 ucm pending, channel 0 / 0xfd, / 1111 1101 ucm pending, channel 1 / 0xfb, / 1111 1011 ucm pending, channel 2 / 0xf7, / 1111 0111 ucm pending, channel 3 / 0xef, / 1110 1111 ucm pending, channel 4 / 0xdf, / 1101 1111 ucm pending, channel 5 / 0xbf, / 1011 1111 ucm pending, channel 6 / 0x7f / 0111 1111 ucm pending, channel 7 / };

/ Modem switching variables / / extern BYTE ADC_Channel; /

// Used during cfg or device info gathering to monitor for presence of 2nd master. BYTE secondary_master_rx_on_cfg[HA_MAX_CHANNEL];

/ The following variables are global HART state machine variables. / static BYTE hasm_loop_cnt; static BYTE ha_cold_start; / cold start / static BYTE ha_enabled_channels; / Which channels are enabled with HART functionality / static BYTE ha_sm_return_ok; / Do state calls within the state machine instead of nested calls. / static BYTE ha_next_input_channel; / Channel data to be stuffed in the input tags next go around / static BYTE ha_switching_channel; / Channel switching in progress? / static BYTE ha_channel; / current state machine channel / static BYTE ha_channel_next; / channel to be switched to / static BYTE ha_channel_prev; / last service channel / static BYTE ha_config_change; / Which channels have configuration change / static BYTE ha_full_scan_mask; / When a full scan is finished, the mask will be 0 / static BYTE ha_full_scan_mask_config; / Full scan mask as configured /

static BYTE ha_scan_counter; / When enough channels scanned to enable pass-through, the counter will be 0 /

//KPK 10/21/2002 - Eliminated this structure as an optimization. // static HA_CONTROL ha_Control[HA_MAX_CHANNEL]; / HART application state machine control structure / // Channel specific state machine variables: static PTR_SM_FUNCTION ha_ctrl_ptr_state_function[HA_MAX_CHANNEL]; / May be changed herein as described in HartAppStateMachine(). / static HART_APP_STATES ha_ctrl_state [HA_MAX_CHANNEL]; static BYTE ha_ctrl_configuration_set [HA_MAX_CHANNEL]; / Configuration received and valid / static BYTE ha_ctrl_hart_enabled [HA_MAX_CHANNEL]; / HART capability enabled / static BYTE ha_ctrl_hart_in_service [HA_MAX_CHANNEL]; / HART channel in normal service mode / static BYTE ha_ctrl_comm_established [HA_MAX_CHANNEL]; / Communication established, meaning unique address available. / static BYTE ha_ctrl_n_xmit_preamble_bytes [HA_MAX_CHANNEL]; / Number of preambles desired by the device. Defaults to 20. / static BYTE ha_ctrl_comm_failed [HA_MAX_CHANNEL]; / Derived from the communication process / static BYTE ha_ctrl_hart_comm_status [HA_MAX_CHANNEL]; / Extracted from the reponse PDU / static BYTE ha_ctrl_hart_device_status [HA_MAX_CHANNEL]; / Extracted from the reponse PDU / static BYTE ha_ctrl_hart_device_status_prev [HA_MAX_CHANNEL]; / Extracted from any reponse PDU including pass-through / static BYTE ha_ctrl_PDU_delimiter [HA_MAX_CHANNEL]; / Out module's delimiter / static BYTE ha_ctrl_next_poll_address [HA_MAX_CHANNEL]; / Next polling address / // BYTE ha_ctrl_device_address [HA_MAX_CHANNEL][5]; / 5-byte unique address / BYTE DeviceLongFormatAddress[HA_MAX_CHANNEL][5]; / 5-byte unique address / static BYTE ha_ctrl_restart_device_info [HA_MAX_CHANNEL]; / Event flag to trigger a new sequence of updating device information / static BYTE ha_ctrl_servicing_global_event [HA_MAX_CHANNEL]; / Flag to indicate special event in service / static BYTE ha_ctrl_more_status_avail [HA_MAX_CHANNEL]; / Flag to indicate more HART device status needed: issue CMD#48 / static BYTE ha_ctrl_reset_config_change [HA_MAX_CHANNEL]; / Flag to indicate reset config is needed: issue CMD#38 / static BYTE ha_ctrl_reset_bursting_device [HA_MAX_CHANNEL]; / Flag to indicate sending burst mode control is needed: issue CMD#109 / static BYTE ha_ctrl_hart_command [HA_MAX_CHANNEL]; / HART command number / static BYTE ha_ctrl_hart_command_sent [HA_MAX_CHANNEL]; / Indicate pending HART command's response is expected / / Supporting Device Information Messaging / static WORD ha_ctrl_dev_info_status [HA_MAX_CHANNEL];

//--- END HA_CONTROL --- static BYTE ha_evctrl_channel; / Channel number / static BYTE ha_evctrl_hart_command; / HART command number / static BYTE ha_evctrl_hart_command_sent; / Indicate pending HART command's response is expected / //--- End HA_EVENT_CONTROL ---

static BYTE ha_allow_hart_comm_fail[HA_MAX_CHANNEL];

static BYTE DeviceConnectedAtPollAddr0; // FIX_COMMFAILURE_ON_DEVICEINFO_UPDATE

/ Pass-thru variables / static UINT ha_handle_timeout; / 1..255 seconds / static BYTE ha_passthru_service_index; // ?? FOR NOW LEAVE THIS

static BYTE mPassThruNextHandle; / Next Available Handle Number / static BYTE mPassThruChannel; static BYTE mPassThruHandle; static T_PASS_THRU_QUEUE_TYPE_E mQueueType; static WORD mPassThruMsgSize; static BYTE mPassThruMsgBuffer[268];

static BYTE mHartChannelStatusByte[HA_MAX_CHANNEL]; static BYTE mConnectsDeviceDataChanged[HA_MAX_CHANNEL];

static REAL mHartDeviceReportedLoopCurrentMa[HA_MAX_CHANNEL];

static BYTE mHartDeviceIsRev5[HA_MAX_CHANNEL]; static BYTE mNumberValidCodesFromCmd50Reply[HA_MAX_CHANNEL]; static BYTE mInitialGatheringOfDeviceInformation[HA_MAX_CHANNEL]; static BYTE mHartCmd48ReplySize[HA_MAX_CHANNEL]; static BYTE mHartCmd9ScanCounter[HA_MAX_CHANNEL];

ifndef DEBUG_REPORT_CURRENT_DIFF_CHECK_DATA

static float mMScaleFactor0To20Ma; static float mBOffsetFactor0To20Ma;

else

float mMScaleFactor0To20Ma; float mBOffsetFactor0To20Ma;

endif

static T_HART_DYNAMIC_VARIABLES_S mHartDynamicVarsStruct[HA_MAX_CHANNEL];

ifdef NO_REPLY_DATA_ONTIME_1234

static BYTE mScanCmdMissedUpdateCounter[HA_MAX_CHANNEL];

endif

ifdef FIX_WRONG_ASSIGNMENT_CODES_USED_FOR_PVS_ONTIME_2387

static T_HART_PVs_ASSIGNMENT_CODES_S mPvSvTvFvAssignmentCodesForCmd9[HA_MAX_CHANNEL]; static BYTE mHartRev[HA_MAX_CHANNEL]; static BYTE mLastDeviceVarCode[HA_MAX_CHANNEL]; static BYTE mForceUseOfCmd3[HA_MAX_CHANNEL];

endif

/ Device Information Messaging variables / /* The HART I/O modules are required to automatically detect device configuration change. Device configuration change can be obtained by using the masking 0x40 with the device status. Device status is the 2nd status byte returned with each device response PDU. There are several control variables utilized in this file. They are: Variable Name Scope Description ----------------------------- ------ --------------------------------------------------- ha_pending_update_device_info Global Indicating device information update is in progress. Upon detection of device configuration change, a bit corresponding to the channel number will be set. It is cleared after the entire table of device info is updated. ha_dev_info_avail Global This is a 16-bit word showing the bit mask pattern when all commands associated with device information has been serviced. This bit pattern is determined once during initialization. ha_hart_dev_info[] Channel For each given channel, this is the device information for the connected HART field device. Information obtained from various HART commands are stored here. ha_ctrl_ha_ctrl_reset_config_change Channel Upon first detection of configuration change, the flag ha_ctrl_hart_device_status_prev .reset_config_change for a given channel will be set. reset_config_change will trigger global event to be serviced, thus sending a HART command #38 out to reset the device configuration change bit. Every time, a valid slave response if received, it is stored in the variable .hart_device_status_prev. Only a positive transition from 0 to 1 for bit 6 in the device status will cause the .reset_config_change to be set, and a new sequence of device information update be initiated. ha_ctrl_restart_device_info Channel It is possible that while device information is being updated, a device configuration change comes through. In this case, the sequence to obtain device information must be restarted. The channel control variable: ha_ctrl_restart_device_info flag which will trigger the * event for updating device information. / static BYTE ha_pending_update_device_info; / Which channels need device info update / static WORD ha_dev_info_avail; / bit pattern when all device information is available / static T_HART_DEVICE_INFORMATION_S HartDeviceInfoStruct[HA_MAX_CHANNEL];

/ Keeps Bit4 set after comm failure until re-connection & new device info / static BYTE ha_comm_failed_wait_for_new_device_info;

define HA_INSERVICE_SKIP_SECS 180 // 3 minutes

static BYTE ha_out_of_service_sec_cnts [HA_MAX_CHANNEL]; static BYTE ha_global_out_of_service;

static HA_CMD_48_DATA ha_cmd_48_data [HA_MAX_CHANNEL]; static BYTE cmd48_supported_flags[HA_MAX_CHANNEL]; static BYTE ha_cmd48_sec_cnts [HA_MAX_CHANNEL];

define CMD48_MAX_SKIP_SECS 120 // 2 minutes

static BYTE ha_broadcast_chan; static BYTE ha_channel_update_pending_cnt[HA_MAX_CHANNEL];

define MIN_CFG_CHG_BIT_SET_STABLE_CNTS 10

define MIN_CFG_BIT_CLEAR_PERIOD MS_5000

static short ha_cfg_chg_bit_clr_trigger_ticks0 [HA_MAX_CHANNEL]; static BYTE ha_cfg_chg_bit_clr_trigger [HA_MAX_CHANNEL]; static BYTE ha_cfg_chg_bit_set_cnts [HA_MAX_CHANNEL];

/*****

  • BOOLEAN HaClearCfgChngBitTrigger(BYTE device_status, BYTE channel)
  • Configuration changed bit clear trigger logic:
  • 1) We always want to process a "LO" to "HI" transition.
  • 2) Consider that the cfg chg bit can be stuck "HI" if the device is in write
  • protect mode. We want to process a change to "LO" if the device is
  • removed from write protect mode, but not falsely trigger on a delayed
  • status due to behavior of the clear cfg change bit command/response
  • when not write protected.
  • 3) Also consider a handheld may connect when write protected. Assume,
  • in this case the cfg changed. We have no way of knowing otherwise.
  • 4) During device commissioning via passthrough, multiple changes may be
  • be made that could set the cfg changed bit. Going offline to
  • reconfigure during commissioning could result in errors since module
  • may not be ready to respond.
  • Delay processing of the trigger MIN_CFG_BIT_CLEAR_PERIOD msec to
  • prevent multiple and uneeded processing and to prevent disruption of
  • device commissioning via passthrough interface. *****/ BOOLEAN HaClearCfgChngBitTrigger(BYTE device_status, BYTE channel) { short ticks_diff;

if (0 != (device_status & HPDU_CONFIGURATION_CHANGED)) // cfg chg bit = '1'? { if ((0 == ha_cfg_chg_bit_set_cnts[channel]) // 0 to 1 transition? || (secondary_master_rx_on_cfg [channel])) // OR secondary master present? { ha_cfg_chg_bit_clr_trigger [channel] = 1; // Trigger always. ha_cfg_chg_bit_clr_trigger_ticks0[channel] = GetTicks(); } if (ha_cfg_chg_bit_set_cnts[channel] < MIN_CFG_CHG_BIT_SET_STABLE_CNTS)// Prevent rollover. ha_cfg_chg_bit_set_cnts[channel]++; // Increment stability count. } else // cfg chg bit = '0'. { // Previously a stable '1'? if (ha_cfg_chg_bit_set_cnts[channel] >= MIN_CFG_CHG_BIT_SET_STABLE_CNTS) { ha_cfg_chg_bit_clr_trigger[channel] = 1; // Trigger if prev '1' was stable. ha_cfg_chg_bit_clr_trigger_ticks0[channel] = GetTicks(); } ha_cfg_chg_bit_set_cnts[channel] = 0; // Clear stablity count. }

// Prevent multiple module reconfigurations during commisioning // via passthrough commands or a handheld by delaying trigger. // Trigger delay will restart on multiple triggers and trigger // will not be acted upon until this delay expires. if (ha_cfg_chg_bit_clr_trigger[channel]) { secondary_master_rx_on_cfg[channel] = 0; // OK to clear. We're triggered. CalcTicksSince(ha_cfg_chg_bit_clr_trigger_ticks0[channel],ticks_diff); if (ticks_diff >= MIN_CFG_BIT_CLEAR_PERIOD) ha_cfg_chg_bit_clr_trigger[channel]++; // prevent missing this trigger. if (ha_cfg_chg_bit_clr_trigger[channel] >= 2) { ha_cfg_chg_bit_clr_trigger[channel] = 0; return 1; // Trigger clear cfg chg bit now. } // Else, wait to process the new trigger later when the module is not so busy. } return 0; // Do not trigger clear cfg chg bit now. }

// // _inline function bodies must be declared prior to referencing it in the file. //

/***

  • void revmemcpy(BYTE dst, BYTE src, WORD size) ***/ _inline void revmemcpy(BYTE dst, BYTE src, WORD size) { src = src + size - 1; for (; size > 0; size--) dst++ = src--; }

/***

  • void BypassVariableScan(BYTE channel)
  • This HA service clears the scanning mask(s) bit positions(s) to immitate
  • actual scanning of variables in case of communication failure, or other
  • non-active states. This is needed to enable pass through. For enhanced
  • pass-through, the function decrements the scan counter. ***/ _inline void BypassVariableScan(BYTE channel) { ha_ctrl_dev_info_status[channel] |= HA_DEV_INFO_CMD3; ha_full_scan_mask &= bitmasks_clear[channel]; }

/***

  • void UpdateDeviceInformation(BYTE channel)
  • This HA service clears currently stored device information and setup
  • global event mask to trigger new sequence of getting device information. ***/ _inline void UpdateDeviceInformation(BYTE channel) { / Update Device Information - Initiate a new sequence of device information gathering / / Clear up existing device information regardless of what's in it. / ClearDeviceInfo(channel);

/ Initiate a new sequence by wiping out the old information except for the address / if (ha_ctrl_state[channel] != HA_SM_CONNECTING) HaSmGetDeviceInformation(channel, SM_INITIALIZE); if (ha_pending_update_device_info & bitmasks[channel]) { / Already in the middle of getting device information, needs to restart the process. / ha_ctrl_restart_device_info[channel] = TRUE; } else ha_pending_update_device_info |= bitmasks[channel]; }

/***

  • void ScheduleChannelUpdate(BYTE channel) ***/ _inline void ScheduleChannelUpdate(BYTE channel) { if (ha_channel_update_pending_cnt[channel] < 0xFF)// Prevent rollunder. ha_channel_update_pending_cnt[channel]++; // Add a new request. }

/***

  • void UpdateChannelStatus(void)
  • Called every state machine cycle. Sends next pending channel update if
  • any. ***/ extern WORD BroadcastSequenceNumber; extern BYTE IncrementSequenceNumber; void UpdateChannelStatus(void) { BOOLEAN ea; BYTE index; BYTE *hip; static BYTE channel; static WORD sequence_number;

// Allow packet change? (Previous packet present for at least 1 RPI)? ea = EA; // save interrupt EA = 0; // disable interrupt if (sequence_number == BroadcastSequenceNumber) { EA = ea; // restore interrupt return; // Not held long enough. Hold this display. } else EA = ea; // restore interrupt

{ // Yes, see if we have any channels pending. for (index = 0; index < HA_MAX_CHANNEL; index++) // Check all n channels { if (++channel >= HA_MAX_CHANNEL) // Next channel channel = 0; if (ha_channel_update_pending_cnt[channel] > 0) // Requests pending? { goto update_channel_packet; } } return; // If we got here, no channels pending. }

update_channel_packet:

ifdef DEBUG_REPORT_CURRENT_DIFF_CHECK_DATA

if( channel !=0 ) { return; }

endif

if( ha_ctrl_hart_enabled[channel] ) { if ( (!ha_ctrl_comm_established[channel]) || ha_ctrl_comm_failed[channel] || (ha_pending_update_device_info & bitmasks[channel]) || (ha_comm_failed_wait_for_new_device_info & bitmasks[channel]) ) { mHartChannelStatusByte[channel] |= HART_CHAN_STATUSBITS_INITIALIZING; } else { mHartChannelStatusByte[channel] &= ~HART_CHAN_STATUSBITS_INITIALIZING; } }else{ mHartChannelStatusByte[channel] &= ~HART_CHAN_STATUSBITS_INITIALIZING; }

mHartDynamicVarsStruct[channel].HARTCommandStatus = ha_ctrl_hart_comm_status[channel]; mHartDynamicVarsStruct[channel].HARTFieldDeviceStatus = ha_ctrl_hart_device_status[channel];

/ Stuff HART data into the input tags / hip = (BYTE *)&plc_input_blk.HartInputStruct;

ea = EA; // save interrupt EA = 0; // disable interrupt IncrementSequenceNumber = TRUE; // Increment Sequence Number

memcpy((BYTE )&plc_input_blk.HartInputStruct.aHartVarsStruct[channel].HartPv, &mHartDynamicVarsStruct[channel].HartPvValue, 4(sizeof(REAL)) );

plc_input_blk.HartInputStruct.aHartChanStatusStruct[channel].StatusBits = mHartChannelStatusByte[channel];

ifdef NO_REPLY_DATA_ONTIME_1234

if( !ha_ctrl_hart_in_service[channel] || (mHartChannelStatusByte[channel] & HART_CHAN_STATUSBITS_FAIL) || (mHartDynamicVarsStruct[channel].HARTFieldDeviceStatus & 0x8D) || (mHartDynamicVarsStruct[channel].HartPvStatus == 0) )

else

if( !ha_ctrl_hart_in_service[channel] || (mHartChannelStatusByte[channel] & HART_CHAN_STATUSBITS_FAIL) || (mHartDynamicVarsStruct[channel].HARTFieldDeviceStatus & 0x8D) )

endif

{ plc_input_blk.module_status |= (MODS_CHAN_0_HART_FAULT << channel); }else{ plc_input_blk.module_status &= ~(MODS_CHAN_0_HART_FAULT << channel); }

plc_input_blk.HartInputStruct.aHartChanStatusStruct[channel].CommStatus = mHartDynamicVarsStruct[channel].HARTCommandStatus; plc_input_blk.HartInputStruct.aHartChanStatusStruct[channel].DeviceStatus = mHartDynamicVarsStruct[channel].HARTFieldDeviceStatus;

ifdef ADD_EXT_DEVICE_STATUS_IN_INPUT_BLOCK

plc_input_blk.HartInputStruct.aHartChanStatusStruct[channel].ExtDeviceStatus = mHartDynamicVarsStruct[channel].HartExtDeviceStatus;

endif

plc_input_blk.HartInputStruct.aHartVarsStruct[channel].HartPvStatus = mHartDynamicVarsStruct[channel].HartPvStatus; plc_input_blk.HartInputStruct.aHartVarsStruct[channel].HartSvStatus = mHartDynamicVarsStruct[channel].HartSvStatus; plc_input_blk.HartInputStruct.aHartVarsStruct[channel].HartTvStatus = mHartDynamicVarsStruct[channel].HartTvStatus; plc_input_blk.HartInputStruct.aHartVarsStruct[channel].HartFvStatus = mHartDynamicVarsStruct[channel].HartFvStatus;

// Timestamp for new packet data page: sequence_number = BroadcastSequenceNumber;

EA = ea; // Allow interrupts now. } /***

  • void CommFailure(void)
  • This HA service reset state machine variables to handle communiation faults. ***/ _inline void CommFailure(void) { ResetHartData(ha_channel);

/ Flag that channel has comm failure until re-connection & new device info / ha_comm_failed_wait_for_new_device_info |= bitmasks[ha_channel];

HaSmConnecting(ha_channel, SM_INITIALIZE); / Start over next time with new connection sequence / BypassVariableScan(ha_channel); ha_ctrl_comm_failed[ha_channel] = TRUE; ScheduleChannelUpdate(ha_channel); ManageChannels(SM_RECOVERING); / Let the HART channel manager decide what to do next? / }

/*****

  • void ScheduleCmd48 (BYTE channel) *****/ _inline void ScheduleCmd48 (BYTE channel) { ha_ctrl_more_status_avail[channel] = TRUE; HaSmServiceGlobalEvent(channel, SM_INITIALIZE); ha_evctrl_hart_command = HA_READ_ADDITIONAL_DEV_STATUS; ha_ctrl_servicing_global_event[channel] = TRUE; }

/*****

  • void CheckForExtendedStatus (BYTE channel) *****/ _inline void CheckForExtendedStatus (BYTE channel) { BYTE extnd_bits_now; ha_ctrl_more_status_avail[channel] = FALSE; // Default to not schedule command 48. if (!ha_ctrl_hart_in_service[channel] == TRUE) {// Out of service: // Make sure these bits show up as new if present when we return to service. ha_ctrl_hart_device_status[channel] &= ~(HPDU_MORE_STATUS_AVAIL | HPDU_DEVICE_MALFUNCTION); } else if ((cmd48_supported_flags[channel]) || (ha_ctrl_hart_device_status[channel] & HPDU_MORE_STATUS_AVAIL)) {// Responded to a command 48 previously OK OR Extended status flag is set: // Change in HPDU_MORE_STATUS_AVAIL OR HPDU_DEVICE_MALFUNCTION flags? extnd_bits_now = ha_ctrl_hart_device_status[channel] & (HPDU_MORE_STATUS_AVAIL | HPDU_DEVICE_MALFUNCTION); if (extnd_bits_now // An extended status bit changed? != (ha_ctrl_hart_device_status_prev[channel] & (HPDU_MORE_STATUS_AVAIL | HPDU_DEVICE_MALFUNCTION))) { if (extnd_bits_now == 0) // No need to read status if there is none. memset (&ha_cmd_48_data[channel], 0, HART_MAX_CMD_48_DATA); else ScheduleCmd48(channel); } // OR an extended status bit is set AND more than n seconds since last cmd48? else if ((extnd_bits_now) && (ha_cmd48_sec_cnts[channel] >= CMD48_MAX_SKIP_SECS)) ScheduleCmd48(channel); } }

/***

  • _inline RESPONSE_CATEGORY CheckCommStatusBytes(BYTE channel)
  • Call this routine to determine HART channel communication status.
  • The two Communication Status Bytes will be extracted and stored in their
  • corresponding local storage: ha_hart_comm_status[channel] and
  • ha_hart_device_status[channel. Overall HART channel health will be
  • determine and stored in ha_comm_established[channel] & ha_comm_failed[channel]
  • A good slave response would always have an echo of the master command in
  • the response PDU. If the command echo is invalid .hart_device_violation
  • will be set. This status will eventually be sent back to the host via the
  • input tags assembly.
  • ***/ _inline RESPONSE_CATEGORY CheckCommStatusBytes(BYTE channel, BYTE pData, BYTE CommandNumber) { / Possible return values are: RESPONSE_SUCCESS, COMM_ERROR, COMMAND_ERROR, COMMAND_NOT_SUPPORTED, RESPONSE_OTHER. / ha_ctrl_hart_comm_status[channel] = pData[HA_RSP_COMM_STATUS]; if (ha_ctrl_hart_comm_status[channel] & HPDU_COMM_ERR) { / Transmission, or reception error / ha_ctrl_comm_failed[channel] = TRUE; ha_ctrl_comm_established[channel] = FALSE; return COMM_ERROR; } else { / Received HART device response PDU without communication error. */ ha_ctrl_comm_failed [channel] = FALSE; ha_ctrl_comm_established[channel] = TRUE;

ha_allow_hart_comm_fail[channel] = 1; // Allow since comm established.

/ Determine device configuration change / ha_ctrl_hart_device_status[channel] = pData[HA_RSP_DEV_STATUS]; if (!ha_ctrl_hart_in_service[channel] == TRUE) { // No Device Configuration Change ha_ctrl_reset_config_change[channel] = FALSE; } else if (HaClearCfgChngBitTrigger(pData[HA_RSP_DEV_STATUS], channel)) { / Device Configuration changed / ha_ctrl_reset_config_change[channel] = TRUE; HaSmServiceGlobalEvent(channel, SM_INITIALIZE); ha_evctrl_hart_command = HA_RESET_CONFIGURATION_CHANGE_BIT; ha_ctrl_servicing_global_event[channel] = TRUE; UpdateDeviceInformation(channel); } else / No Device Configuration Change / ha_ctrl_reset_config_change[channel] = FALSE;

CheckForExtendedStatus(channel);

ha_ctrl_hart_device_status_prev[channel] = ha_ctrl_hart_device_status[channel];

/ Check command number in the response packet / if (CommandNumber != pData[HA_RSP_COMMAND_ECHO]) // Command echo must match { return COMMAND_ERROR; } else { // Response is for the proper HART command: check all common single-definition response codes. switch (ha_ctrl_hart_comm_status[channel]) { case H5_RC_000_SUCCESS: // Success and matching command echoed return RESPONSE_SUCCESS;

case H5_RC_064_CMD_NOT_IMPLEMENTED: return COMMAND_NOT_SUPPORTED;

case H5_RC_032_BUSY: default: // Other non-success response code. / Leave it for the state function to process it according to the command number / return RESPONSE_OTHER; } } } }

/***

  • _inline BYTE FindNextChannel(BYTE current_channel)
  • This function returns next enabled HART channel number if it exists. If
  • not, after looking at all available channels, this function returns the
  • current channel's value. ***/ _inline BYTE FindNextChannel(BYTE current_channel) { BYTE i; for (i = 0; i < HA_MAX_CHANNEL; i++) // Scan all channels once at most starting at next channel. { if (++current_channel == HA_MAX_CHANNEL) current_channel = 0; if ((ha_config_change) || (ha_ctrl_hart_enabled[current_channel])) { if (!ha_ctrl_hart_in_service[current_channel]) ha_sm_return_ok = 1; // Don't get stuck on an out of service channel. break; // Channel found that needs update. Exit loop. } } return current_channel; }

/***

  • _inline void UpdatePassthru(void)
  • Call this service to continue the handle timeout or invalidate handle
  • based on the timeout. ***/ _inline void UpdatePassthru(void) { int channel; BYTE* ChannelsNeedUpdateList;

ChannelsNeedUpdateList = HARTPASSTHRU_UpdateQueueTimers();

if( ChannelsNeedUpdateList != NULL ) { for(channel = 0; channel < 8; channel++) { if( ChannelsNeedUpdateList[channel] ) { / Reset Ladder PassThru Reply Ready Status Bit / mHartChannelStatusByte[channel] &= ~HART_CHAN_STATUSBITS_MSGREADY; ScheduleChannelUpdate(channel); } } } }

/***

  • void ClearDeviceInfo (BYTE channel)
  • Call this service to reset HART channel device information. This should
  • be called when new configuration is received or after HART communication
  • failed. ***/ void ClearDeviceInfo(BYTE channel) { secondary_master_rx_on_cfg[channel] = 0; // Start monitoring for 2nd master.

ha_ctrl_hart_device_status_prev [channel] = 0;

/ Clear all data in device information structure except for CMD 0 data if we are re-gathering device information / if( ha_ctrl_state[channel] == HA_SM_WAIT_TO_CONFIGURE || ha_ctrl_state[channel] == HA_SM_CONNECTING ) { ha_ctrl_dev_info_status[channel] = HA_DEV_INFO_NOT_AVAIL; memset(&HartDeviceInfoStruct[channel], 0, sizeof(T_HART_DEVICE_INFORMATION_S)); }else{ // Don't clear CMD 0 data and flag since we don't want to re-send CMD 0 during Device Information update ha_ctrl_dev_info_status[channel] = HA_DEV_INFO_CMD0;

ifdef ADD_NEW_16BIT_MFG_AND_DEVICE_TYPE_ONTIME_1491

/ Clear all data past CMD 0 data in structure AND Skip the new CMD 0 data placed after TagSize in V2.1 / / which start at offset 102 which is 18 bytes from the end. / / Then clear data from offset 104(PvLowerRangeValue) - 117 which is 14 bytes / / (WARNING: If structure definition changed for elements above TagSize then the subtract) / / ("16" value needs to be changed. ) / memset(&HartDeviceInfoStruct[channel].TagSize, 0, sizeof(T_HART_DEVICE_INFORMATION_S)-(16+18)); memset(&HartDeviceInfoStruct[channel].PvLowerRangeValue, 0, 14);

else

/ Clear all data past CMD 0 data in structure. / / (WARNING: If structure definition changed for elements above TagSize then the subtract) / / ("16" value needs to be changed. ) / memset(&HartDeviceInfoStruct[channel].TagSize, 0, sizeof(T_HART_DEVICE_INFORMATION_S)-16);

endif

}

if (BiteMode) { HartDeviceInfoStruct[channel].PvLowerRangeValue = HART_NaN; HartDeviceInfoStruct[channel].PvUpperRangeValue = HART_NaN; HartDeviceInfoStruct[channel].DampingValue = HART_NaN; } else { HartDeviceInfoStruct[channel].PvLowerRangeValue = 0.0; HartDeviceInfoStruct[channel].PvUpperRangeValue = 0.0; HartDeviceInfoStruct[channel].DampingValue = 0.0; } HartDeviceInfoStruct[channel].PvVariableCode = HART_INVALID_VAR_ASSIGN_CODE; HartDeviceInfoStruct[channel].SvVariableCode = HART_INVALID_VAR_ASSIGN_CODE; HartDeviceInfoStruct[channel].TvVariableCode = HART_INVALID_VAR_ASSIGN_CODE; HartDeviceInfoStruct[channel].FvVariableCode = HART_INVALID_VAR_ASSIGN_CODE; }

/***

  • void ResetHartData (BYTE channel)
  • Call this service to reset HART channel data. This should be called for
  • "channel modem/state machine reset" command is processed or when HART
  • communication failure is detected.
  • WARNING: Do not call this routine unless Device Information needs to be
  • be reset and the state function pointer is set for one of the
  • initial states: HaSmConnecting, HaSamWaitToConfigure, or
  • HaSmGetDeviceInformation. ***/ void ResetHartData(BYTE channel) { BYTE index;

ifdef ADD_NEW_ADDITIONAL_STATUS_BIT

int AllChannelsWithoutHart; int i; BYTE ea;

endif

ha_allow_hart_comm_fail[channel] = 1; // Allow since comm attempted but failed.

mHartDynamicVarsStruct[channel].HARTCommandStatus = ha_ctrl_hart_comm_status[channel]; mHartDynamicVarsStruct[channel].HARTFieldDeviceStatus = ha_ctrl_hart_device_status[channel];

/ Reset device information availability flag. / ha_ctrl_dev_info_status[channel] &= ~HA_DEV_INFO_CMD3;

/ Reset dynamic variable values and Primary Variable Analog Unit / if (BiteMode) { mHartDynamicVarsStruct[channel].HartPvValue = HART_NaN; mHartDynamicVarsStruct[channel].HartSvValue = HART_NaN; mHartDynamicVarsStruct[channel].HartTvValue = HART_NaN; mHartDynamicVarsStruct[channel].HartFvValue = HART_NaN; } else { mHartDynamicVarsStruct[channel].HartPvValue = 0.0; mHartDynamicVarsStruct[channel].HartSvValue = 0.0; mHartDynamicVarsStruct[channel].HartTvValue = 0.0; mHartDynamicVarsStruct[channel].HartFvValue = 0.0; mHartDynamicVarsStruct[channel].HartLoopCurrentValue = 0.0; }

mHartDynamicVarsStruct[channel].HartPvStatus = 0; mHartDynamicVarsStruct[channel].HartSvStatus = 0; mHartDynamicVarsStruct[channel].HartTvStatus = 0; mHartDynamicVarsStruct[channel].HartFvStatus = 0; mHartDynamicVarsStruct[channel].HartExtDeviceStatus = 0;

/ Reset the unit code in the Device Information Buffer / HartDeviceInfoStruct[channel].PvUnitsCode = HART_INVALID_UNIT_CODE; HartDeviceInfoStruct[channel].SvUnitsCode = HART_INVALID_UNIT_CODE; HartDeviceInfoStruct[channel].TvUnitsCode = HART_INVALID_UNIT_CODE; HartDeviceInfoStruct[channel].FvUnitsCode = HART_INVALID_UNIT_CODE;

mHartDynamicVarsStruct[channel].PvUnitsCode = HART_INVALID_UNIT_CODE; mHartDynamicVarsStruct[channel].SvUnitsCode = HART_INVALID_UNIT_CODE; mHartDynamicVarsStruct[channel].TvUnitsCode = HART_INVALID_UNIT_CODE; mHartDynamicVarsStruct[channel].FvUnitsCode = HART_INVALID_UNIT_CODE;

HartDeviceInfoStruct[channel].PvVariableCode = 0; HartDeviceInfoStruct[channel].SvVariableCode = 0; HartDeviceInfoStruct[channel].TvVariableCode = 0; HartDeviceInfoStruct[channel].FvVariableCode = 0; mHartDynamicVarsStruct[channel].PvVariableCode = 0; mHartDynamicVarsStruct[channel].SvVariableCode = 0; mHartDynamicVarsStruct[channel].TvVariableCode = 0; mHartDynamicVarsStruct[channel].FvVariableCode = 0;

mHartChannelStatusByte[channel] = DEFAULT_HART_CHANNEL_STATUS_BYTE_VALUE; mConnectsDeviceDataChanged[channel] = DEFAULT_DEVICE_DATA_CHANGED_VALUE;

mHartDeviceReportedLoopCurrentMa[channel] = 0.0;

mHartDeviceIsRev5[channel] = 1; mNumberValidCodesFromCmd50Reply[channel] = 0; mInitialGatheringOfDeviceInformation[channel] = 1; mHartCmd48ReplySize[channel] = 0; mHartCmd9ScanCounter[channel] = 0;

ifdef NO_REPLY_DATA_ONTIME_1234

mScanCmdMissedUpdateCounter[channel] = 0;

endif

ifdef ADD_NEW_ADDITIONAL_STATUS_BIT

AllChannelsWithoutHart = 1; // TBD IS THIS NECESSARY, MAYBE WE DON'T NEED TO CLEAR BIT for(i=0;i<NUM_ANALOG_CHANNELS;i++) { if( ha_ctrl_comm_established[i] ) { AllChannelsWithoutHart = 0; break; } } if( AllChannelsWithoutHart ) { ea = EA; EA = 0; plc_input_blk.module_status &= ~(MODS_NEW_ADDITIONAL_STATUS); EA = ea; }

endif

ifdef FIX_WRONG_ASSIGNMENT_CODES_USED_FOR_PVS_ONTIME_2387

mHartRev[channel] = 0; mLastDeviceVarCode[channel] = 0; mForceUseOfCmd3[channel] = 0;

endif

/* We might want to consider reset the scanning process when this happens. Other consideration should include: tossing pending pass-through or obtaining device information. The host could use this command to make the I/O module perform a "warm-start" thus causing all channels to be updated with latest * information without closing the connection or resending the configuration tags. / }

static BYTE ha_bite_state; static BYTE ha_bite_chan_under_test; static BYTE ha_bite_status; static float ha_bite_data[HA_MAX_CHANNEL];

/****

  • void HaBiteStateMachine (void)
  • UCM_HART_BITE_TEST:
  • Enter BITE mode by requesting BITE on any valid channel number. msg[0] = {0..7}.
  • Exit BITE mode by requesting BITE on channel number -1 (FFhex). msg[0] = -1.
  • msg[1] = 100
  • Expected usage:
  • 1) User configures system with no channels enabled for HART.
  • 2) User setups a channel for test.
  • 3) User requests a test on that channel.
  • 4) User polls for status.
  • 5) Repeat for all channels.
  • Send msg[0]: Channel: 0, 1, 2, 3, 4, 5, 6, 7 Exit BITE mode: FFhex -1dec
  • Send msg[1]: 100
  • Receive msg...[0]: Channel SINT: -1, 0, 1, 2, 3, 4, 5, 6, 7
  • Receive msg...[1]: Status SINT: BUSY(34) SUCCESS(0) DR_DEAD(35)
  • Receive msg[ 2.. 5]: channel 0 data REAL (1#QNaN if not read or faulted).
  • Receive msg[ 6.. 9]: channel 1 data REAL (1#QNaN if not read or faulted).
  • Receive msg[10..13]: channel 2 data REAL (1#QNaN if not read or faulted).
  • Receive msg[14..17]: channel 3 data REAL (1#QNaN if not read or faulted).
  • Receive msg[18..21]: channel 4 data REAL (1#QNaN if not read or faulted).
  • Receive msg[22..25]: channel 5 data REAL (1#QNaN if not read or faulted).
  • Receive msg[26..29]: channel 6 data REAL (1#QNaN if not read or faulted).
  • Receive msg[30..33]: channel 7 data REAL (1#QNaN if not read or faulted). ****/ void HaBiteStateMachine (void) { static short ticks0; short ticksdiff; RESPONSE_CATEGORY response_status; BYTE i;

switch(ha_bite_state) { case HAB_INIT_BITE: for (i = 0; i < HA_MAX_CHANNEL; i++) ha_bite_data[i] = HART_NaN; ha_bite_state = HAB_SEND_CMD0; // Fall through to next case

case HAB_SEND_CMD0: if (!RequestHartChannelSwitch(ha_bite_chan_under_test)) break; // Channel switch not granted. Try again later.

hu_channel = 0; // Always use channel 0 HART data structures.

//make sure Poll Address 0 is sent ha_ctrl_next_poll_address[hu_channel] = POLL_ADDRESS_0 | HPDU_MASTER_ADDRESS; addr_group_attempts[hu_channel] = 0;

SendHartCommand(hu_channel, HA_READ_UNIQUE_ID, SM_NORMAL); ticks0 = GetTicks(); ha_bite_state = HAB_WAIT_CMD0; break;

case HAB_WAIT_CMD0: if ((hu_state != HU_FAULTED) && (hu_rx_size != 0)) { response_status = ParseResponse(hu_channel, hu_rx_msg); if (RESPONSE_SUCCESS == response_status) ha_bite_state = HAB_SEND_CMD3; else { ha_bite_state = HAB_FAULTED; ha_bite_status = FAULTED; } } else { CalcTicksSince(ticks0,ticksdiff); if (ticksdiff >= HART_BITE_TIMEOUT) { ha_bite_state = HAB_FAULTED; ha_bite_status = FAULTED; } } break;

case HAB_SEND_CMD3: if (!RequestHartChannelSwitch(ha_bite_chan_under_test)) break; // Channel switch not granted. Try again later. hu_channel = 0; // Always use channel 0 HART data structures. SendHartCommand(hu_channel, HART_CMD_3_READ_DYNAMIC_VARS, SM_NORMAL); ticks0 = GetTicks(); ha_bite_state = HAB_WAIT_CMD3; break;

case HAB_WAIT_CMD3: if (hu_state == HU_FAULTED) ha_bite_state = HAB_FAULTED; else if (hu_rx_size != 0) { response_status = ParseResponse(hu_channel, hu_rx_msg); if (RESPONSE_SUCCESS == response_status) { ha_bite_data[ha_bite_chan_under_test] = mHartDynamicVarsStruct[hu_channel].HartPvValue; ha_bite_status = SUCCESS; ha_bite_state = HAB_IDLE; } else { ha_bite_state = HAB_FAULTED; ha_bite_status = FAULTED; } } else { CalcTicksSince(ticks0,ticksdiff); if (ticksdiff >= HART_BITE_TIMEOUT) { ha_bite_state = HAB_FAULTED; ha_bite_status = FAULTED; } } break;

case HAB_FAULTED: if (BiteMode) ha_bite_data[ha_bite_chan_under_test] = HART_NaN; else ha_bite_data[ha_bite_chan_under_test] = 0.0; ha_bite_status = DR_DEAD; break;

case HAB_IDLE: // Do nothing. Waiting for a new command. break;

case HAB_DISABLED: default: // Indeterminate state. Reinit HART and BITE. Will exit BITE mode. HA_InitializeApp(1); break; }; }

/***

  • void HA_InitializeApp(BYTE reconfigure)
  • Call this service only after cold start or new connection with a host.
  • This routine will reset everything as if the module is powering up from
  • a cold-start. ***/ void HA_InitializeApp(BYTE reconfigure) { BYTE index; BYTE ea; // Prevent UCM multiple thread conflict.

HartUcmDisable(); // Prevent UCM multiple thread conflict.

/ Initialize UART / HU_UartInit();

/ Initialize HART application / hasm_loop_cnt = 0; ha_cold_start = TRUE; ha_enabled_channels = 0; ha_next_input_channel = 0; / Start with channel 0 for PLC input / ha_config_change = 0; / No configuration changes / ha_pending_update_device_info = 0; ha_switching_channel = 0;

/ Clear comm failure until re-connection & new device info / ha_comm_failed_wait_for_new_device_info = 0;

memset(ha_out_of_service_sec_cnts, 0, HA_MAX_CHANNEL); ha_global_out_of_service = 0;

ha_broadcast_chan = 0xFF; memset(cmd48_supported_flags, 0, HA_MAX_CHANNEL); memset(ha_channel_update_pending_cnt, 1, HA_MAX_CHANNEL); memset(ha_cmd48_sec_cnts, 0, HA_MAX_CHANNEL);

memset(addr_group_attempts, 0, HA_MAX_CHANNEL);

memset(ha_cfg_chg_bit_clr_trigger, 0, HA_MAX_CHANNEL); memset(ha_cfg_chg_bit_set_cnts, 0, HA_MAX_CHANNEL);

/ Initialize global event control structure / HaSmServiceGlobalEvent(0, SM_INITIALIZE); ha_evctrl_channel = HU_NO_BURST_CHANS; ha_full_scan_mask_config = HART_ALL_CHANS_MASK; ha_full_scan_mask = ha_full_scan_mask_config;

PtSetScanCnt();

memset(&mHartChannelStatusByte[0],DEFAULT_HART_CHANNEL_STATUS_BYTE_VALUE,HA_MAX_CHANNEL); memset(&mConnectsDeviceDataChanged[0], DEFAULT_DEVICE_DATA_CHANGED_VALUE,HA_MAX_CHANNEL);

memset(&mHartDeviceIsRev5[0],1,sizeof(BYTE) HA_MAX_CHANNEL); memset(&mNumberValidCodesFromCmd50Reply[0],0,sizeof(BYTE) HA_MAX_CHANNEL); memset(&mInitialGatheringOfDeviceInformation[0],1,sizeof(BYTE) HA_MAX_CHANNEL); memset(&mHartCmd48ReplySize[0],0,sizeof(BYTE) HA_MAX_CHANNEL); memset(&mHartCmd9ScanCounter[0],0,sizeof(BYTE) * HA_MAX_CHANNEL);

ifdef FIX_WRONG_ASSIGNMENT_CODES_USED_FOR_PVS_ONTIME_2387

memset(&mHartRev[0],0,sizeof(BYTE) HA_MAX_CHANNEL); memset(&mLastDeviceVarCode[0],0,sizeof(BYTE) HA_MAX_CHANNEL); memset(&mForceUseOfCmd3[0],0,sizeof(BYTE) * HA_MAX_CHANNEL);

endif

/ AI module device information requires: Commands 0, 12, 13, 16, 50, and 3. / ha_dev_info_avail = (HA_DEV_INFO_CMD0 | HA_DEV_INFO_CMD12 | HA_DEV_INFO_CMD13 | HA_DEV_INFO_CMD15 | HA_DEV_INFO_CMD16 | HA_DEV_INFO_CMD48 | HA_DEV_INFO_CMD50 | HA_DEV_INFO_CMD38 | HA_DEV_INFO_CMD3);

// Passthrough variables HARTPASSTHRU_InitPassThruQueues(0); ha_passthru_service_index = PASSTHRU_QUEUE_SIZE; mPassThruNextHandle = 1; ha_handle_timeout = 0;

// Provide FTP Support ha_bite_chan_under_test = 0xFF; // Default to a non-existant channel so change will be sensed. ha_bite_state = HAB_DISABLED; ha_bite_status = DR_DEAD;

for (index = 0; index < HA_MAX_CHANNEL; index++) { if (BiteMode) ha_bite_data[index] = HART_NaN; else ha_bite_data[index] = 0.0; }

for (ha_channel = 0; ha_channel < HA_MAX_CHANNEL; ha_channel++) { / Initialize state machine control variables / HaSmWaitToConfigure(ha_channel, SM_INITIALIZE); ha_ctrl_configuration_set[ha_channel] = FALSE; }

/ Start off with channel 0 / ha_channel = 0; ha_channel_next = 0; ha_channel_prev = 0;

HaSmWaitToSwitchChannel(ha_channel, SM_INITIALIZE); ScheduleChannelUpdate(ha_channel);

if (TARGET == IF8H)

/ Calculate 0-20ma scaling factor / mMScaleFactor0To20Ma = DIV_FP(SUB_FP(eu_max_scale[RNG_0_20mA], eu_min_scale[RNG_0_20mA]), L2FP(adc_max_scale[RNG_0_20mA] - adc_min_scale[RNG_0_20mA])); / Y = MX + B B = Y - MX / mBOffsetFactor0To20Ma = SUB_FP(eu_max_scale[RNG_0_20mA], MUL_FP(mMScaleFactor0To20Ma, L2FP(adc_max_scale[RNG_0_20mA])));

elif (TARGET == OF8H)

/ Calculate 0-20ma scaling factor / mMScaleFactor0To20Ma = DIV_FP(SUB_FP(eu_max_scale[RNG_0_20mA], eu_min_scale[RNG_0_20mA]), L2FP(dac_max_scale[RNG_0_20mA] - dac_min_scale[RNG_0_20mA])); / Y = MX + B B = Y - MX / mBOffsetFactor0To20Ma = SUB_FP(eu_max_scale[RNG_0_20mA], MUL_FP(mMScaleFactor0To20Ma, L2FP(dac_max_scale[RNG_0_20mA])));

endif

HartUcmEnable(); // Prevent UCM multiple thread conflict.

DeviceConnectedAtPollAddr0 = 0;

if (reconfigure) HA_ConfigureApp(&mod_cfg_blk); }

/***

  • void HA_ConfigureApp (MOD_CFG_BLOCK *pcb)
  • This is the HART application service call from the Assembly Object after
  • receive the configuration tags. This call is asynchronous to the
  • execution of the HART state machine, thus requiring state functions to
  • check for event and take actions accordingly. ***/ void HA_ConfigureApp (MOD_CFG_BLOCK pcb) { BYTE i; BYTE index; CHAN_CFG_BLK pcc = NULL; BYTE ea; // Prevent UCM multiple thread conflict.

BYTE new_hart_enable;

HartUcmDisable(); // Prevent UCM multiple thread conflict.

ha_enabled_channels = 0; ha_full_scan_mask_config = HART_ALL_CHANS_MASK;

PtSetScanCnt();

/ Setup HART Passthrough Handle Timeout value / ha_handle_timeout = pcb->handle_timeout; HARTPASSTHRU_SetHandleTimeout(ha_handle_timeout);

if (ha_cold_start == TRUE) { ha_handle_timeout = pcb->handle_timeout; for (i = 0; i < HA_MAX_CHANNEL; i++) { pcc = &pcb->chan_cfg_blk[i]; if (pcc->cfg_bits & CCFGB_ENABLE_HART) / HART Enabled? / { / bit 7 for Input Module; bit 9 for Output Module / ha_ctrl_hart_enabled[i] = TRUE; ha_enabled_channels |= bitmasks[i]; } else { ha_ctrl_hart_enabled[i] = FALSE; ha_enabled_channels &= bitmasks_clear[i]; ha_full_scan_mask_config &= bitmasks_clear[i]; } ha_config_change = ha_config_change | bitmasks[i]; } ha_cold_start = FALSE; / Next time it won't be a cold start / goto config_exit; }

/ Must check for change of configuration / for (i = 0; i < HA_MAX_CHANNEL; i++) { pcc = &pcb->chan_cfg_blk[i];

if (pcc->cfg_bits & CCFGB_ENABLE_HART) / bit 9 / { new_hart_enable = TRUE; ha_enabled_channels |= bitmasks[i]; } else { ha_allow_hart_comm_fail[i] = 0; // Disallow since we aren't using this channel. new_hart_enable = FALSE; ha_enabled_channels &= bitmasks_clear[i]; ha_full_scan_mask_config &= bitmasks_clear[i]; } if (IsConnectionActive() == FALSE) / New connection? / { ha_ctrl_hart_in_service[i] = TRUE; / Reset service mode / } pcc = &pcb->chan_cfg_blk[i]; if (ha_ctrl_hart_enabled[i] != new_hart_enable) / Hart enable/disable changed / { // This code assums same scan rate of 1X for both dynamic and device variables. // When HART data scheduling becomes a requirement, this must be rewritten. HaSmWaitToConfigure(i, SM_INITIALIZE); ha_ctrl_hart_enabled[i] = new_hart_enable; ha_config_change = ha_config_change | bitmasks[i]; HARTPASSTHRU_ClearAllQueuesForChannel(i); } }

config_exit: ha_full_scan_mask = ha_full_scan_mask_config; PtSetScanCnt();

if (ha_enabled_channels == 0) ScheduleChannelUpdate(0);

HartUcmEnable(); // Prevent UCM multiple thread conflict. }

/****

  • void HaSuspendChannel (BYTE channel)
  • Take the indicated channel out of service. ****/ _inline void HaSuspendChannel (BYTE channel) { BYTE ea;

ha_ctrl_hart_in_service[channel] = FALSE;

/* Reset dynamic variable values to NANs. Do not call ResetHartData() because

  • it wipes out device information and indirectly causes ManageChannels() to
  • stay with the current state for the given channel. */ if (BiteMode) { mHartDynamicVarsStruct[channel].HartPvValue = HART_NaN; mHartDynamicVarsStruct[channel].HartSvValue = HART_NaN; mHartDynamicVarsStruct[channel].HartTvValue = HART_NaN; mHartDynamicVarsStruct[channel].HartFvValue = HART_NaN; } else { mHartDynamicVarsStruct[channel].HartPvValue = 0.0; mHartDynamicVarsStruct[channel].HartSvValue = 0.0; mHartDynamicVarsStruct[channel].HartTvValue = 0.0; mHartDynamicVarsStruct[channel].HartFvValue = 0.0;

mHartDynamicVarsStruct[channel].HartPvStatus = 0; mHartDynamicVarsStruct[channel].HartSvStatus = 0; mHartDynamicVarsStruct[channel].HartTvStatus = 0; mHartDynamicVarsStruct[channel].HartFvStatus = 0; }

ScheduleChannelUpdate(channel); }

/***

  • void HaResumeChannel (BYTE channel) ***/ _inline void HaResumeChannel (BYTE channel) { BYTE ea; ha_ctrl_hart_in_service[channel] = TRUE; ScheduleChannelUpdate(channel); }

int MODULECOMMAND_BiteServices(struct UCMESSAGE_STRUCT MsgStructPtr) { BYTE pRequestData; BYTE pReply; float pBiteData; BYTE Command, channel; int Status; BYTE ea; // Prevent multiple thread conflict. int i;

Status = OK; HartUcmDisable(); // Prevent multiple thread conflict.

pRequestData = (BYTE)MsgStructPtr->MSGDataPtr; pReply = (BYTE)MsgStructPtr->MSGReplyDataPtr;

Command = pRequestData[UCM_COMMAND]; channel = pRequestData[UCM_CHANNEL];

if( BiteMode ) { switch(Command) { case UCM_HART_BITE_TEST: { if (ha_bite_state == HAB_DISABLED) // Just entered BITE mode? { ha_bite_status = BUSY; ha_bite_state = HAB_INIT_BITE; // Then need init. }

if (channel < HA_MAX_CHANNEL) // Valid channel? { if (channel != ha_bite_chan_under_test) // New chan — You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/terryyin/lizard/issues/233#issuecomment-406747799, or mute the thread https://github.com/notifications/unsubscribe-auth/AAwJYrcZ37tGsoh1yBN2qPQUUw43eOxnks5uImJkgaJpZM4VXSW7.

geoland1st commented 6 years ago

Hi Terry,

Thanks for looking into this for me. It looks like we have our answer then as to why there are differences.

Take care,

George

From: Terry Yin notifications@github.com Sent: Saturday, July 21, 2018 18:01 To: terryyin/lizard lizard@noreply.github.com Cc: Anderson, George ganderson@spectrumcontrols.com; Mention mention@noreply.github.com Subject: Re: [terryyin/lizard] McCabe numbers for C code are too high when compared with other commercial tools (#233)

Hi George,

Thanks for sharing the code. Yes, just as I mentioned previously, it’s because of the preprocessor directives.

I started Lizard when I was working in Nokia in my spare time, to replace a commercial tool we were using. And it was called HFCCA — Header Free Cyclomatic Complexity Analyzer. It doesn’t depend the header file, and just treat header files as source code. Therefore it cannot parse preprocessor directive properly. The good side of this tradeoff is, Lizard shows how complicated the.code looks, from the human reader’s perspective, not the compiler’s perspective after preprocessing.

Terry

On 21 Jul 2018, at 7:06 AM, geoland1st notifications@github.com<mailto:notifications@github.com> wrote:

Okay, code is attached. The ‘-Ecpre’ dropped the CCN to 119, which is closer, but not matching the other tools score of 101.

  • George

From: Terry Yin notifications@github.com<mailto:notifications@github.com> Sent: Thursday, July 19, 2018 23:05 To: terryyin/lizard lizard@noreply.github.com<mailto:lizard@noreply.github.com> Cc: Anderson, George ganderson@spectrumcontrols.com<mailto:ganderson@spectrumcontrols.com>; Mention mention@noreply.github.com<mailto:mention@noreply.github.com> Subject: Re: [terryyin/lizard] McCabe numbers for C code are too high when compared with other commercial tools (#233)

George, thanks and please send.

If it’s because of the preprocessor, -Ecpre might improve the result a bit. It will choose the first branch in #if only. But personal opinion, I think #if should be counted as CCN.

On 20 Jul 2018, at 12:49 PM, geoland1st notifications@github.com<mailto:notifications@github.com<mailto:notifications@github.com%3cmailto:notifications@github.com>> wrote:

Hi Terry,

Thanks for your quick response. I will send you the code in my morning (maybe your evening). I expect you are right about the preprocessor conditionals. Any way to get around it? I wonder what those other tools do. I do believe you have made a useful tool. I discovered it because a large company I work with uses it.

George Anderson

On Jul 19, 2018, at 9:02 PM, Terry Yin notifications@github.com<mailto:notifications@github.com<mailto:notifications@github.com%3cmailto:notifications@github.com<mailto:notifications@github.com%3cmailto:notifications@github.com%3cmailto:notifications@github.com%3cmailto:notifications@github.com>>> wrote:

Hi @geoland1sthttps://github.com/geoland1st, thanks for using Lizard. Lizard has been serving many users for many years. I guess it's result is useful:-) Could you give me an example (obfuscate it if it's sensitive) so that I can investigate?

Right now, my guess is it's because of the pre-processor directives. Lizard will count #if as a condition.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHubhttps://github.com/terryyin/lizard/issues/233#issuecomment-406482837, or mute the threadhttps://github.com/notifications/unsubscribe-auth/AniQx8bBPFy-GsK3PxtkLTwNwU01h651ks5uIVZNgaJpZM4VXSW7. — You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/terryyin/lizard/issues/233#issuecomment-406488174, or mute the thread https://github.com/notifications/unsubscribe-auth/AAwJYlTrQE8mjJYX_8CWWWn70rj7d7lvks5uIWFjgaJpZM4VXSW7.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHubhttps://github.com/terryyin/lizard/issues/233#issuecomment-406498178, or mute the threadhttps://github.com/notifications/unsubscribe-auth/AniQxyOnnAPh1ljFgil96zN2FPbZUetHks5uIXMigaJpZM4VXSW7.

/***

  • $Archive: M:\PVCS\pvcs-ver65\archives\1756 HART Brand Label\appsrc\hartapp.c-arc $
  • $Workfile: hartapp.c $
  • $Revision: 1.0 $
  • $Author: gbrown $
  • $Date: 29 Jan 2007 10:12:48 $
  • $Modtime: 24 Jan 2007 15:37:58 $
  • Copyright (c) 2002-2007 Spectrum Controls, Inc.
  • P.O. Box 5533
  • Bellevue, WA 98006
  • Phone: (425) 746-9481
  • CONFIDENTIAL INFORMATION:
  • Spectrum Controls Inc, has proprietary rights to the information
  • contained hereon. This information is to remain the property of
  • Spectrum Controls Inc, and may be used only in conformance with
  • written instructions issued by and from Spectrum Controls Inc.
  • This information is issued in strict confidence.
  • It may not be reproduced in any way without prior written
  • permission from Spectrum Controls Inc, and may be changed and/or
  • recalled without notice.
  • Processor: Philips 8051XA G3 (P51XAG30KBA)
  • Compiler: Tasking C V4.0 r1
  • Products: 1756 Analog Modules with HART
  • Abstract:
  • History:
  • $Log: M:\PVCS\pvcs-ver65\archives\1756 HART Brand Label\appsrc\hartapp.c-arc $
  • Rev 1.0 29 Jan 2007 10:12:48 gbrown
  • Initial revision.
  • ***/

    include

include "..\sharedsrc\basetype.h"

include "APPConnectionManager.h"

include "fpmath.h"

include "hartuart.h" / HART LLC definition /

include "hartapp.h" / HART App definition /

include "timers.h"

include "scales.h"

include "HartPassThru.h"

extern DWORD raw_adc_data[]; extern NEAR WORD dac_data_hart[];

define HART_BITE_TIMEOUT MS_3000

define BiteMode (BITE_JUMPER == 0)

// Polling address 0 is default. 1..15 disable device from driving its analog output.

define N_ADDR_BETWEEN_0_POLL 2 // 0-1-2,0-3-4,0-5-6,0-7-8,etc.

static BYTE addr_group_attempts[HA_MAX_CHANNEL];

ifdef INCREASE_MAX_POLL_ADDR_TO_63_ONTIME_1676

define START_LOW_POLL_ADDRESS 1

define MAX_LOW_POLL_ADDRESS 15

define START_HI_POLL_ADDRESS 16

define MAX_HI_POLL_ADDRESS 63

static BYTE LowPollAddress[HA_MAX_CHANNEL]; // 1-15 static BYTE HiPollAddress[HA_MAX_CHANNEL]; // 16-63 static BYTE LowPollDone[HA_MAX_CHANNEL]; // Flag indicates last poll done was the low poll, time for hi poll

endif

define PtSetScanCnt() \

{ \ if (mod_cfg_blk.module_cfg_bits & 0x8000) \ ha_scan_counter = 1; \ else if (!(mod_cfg_blk.module_cfg_bits & 0x4000)) \ ha_scan_counter = 2; \ }

define PtDecScanCount() \

{ \ if (ha_scan_counter > 0) \ ha_scan_counter--; \ }

define PtPriorityMode() (mod_cfg_blk.module_cfg_bits & 0x8000)

define PtTrigger() \

((((mod_cfg_blk.module_cfg_bits & 0x8000) || \ !(mod_cfg_blk.module_cfg_bits & 0x4000)) && (ha_scan_counter == 0)) || \ (ha_full_scan_mask == 0))

// The Unconnected message handler: HA_ProcessUnconnectedMsg() // is called via the timer 1 interrupt service routine. // The HART application state machine is in the main thread. // Use the following macros to prevent contention between these two threads. BOOLEAN hart_ucm_enabled = 1;

define HartUcmDisable() \

{ \ ea = EA; / Save global interrupt enable. / \ EA = 0; / Disable all interrupts. / \ hart_ucm_enabled = 0; \ EA = ea; / Reenable all other interrutps. / \ }

define HartUcmEnable() \

{ \ ea = EA; / Save global interrupt enable. / \ EA = 0; / Disable all interrupts. / \ hart_ucm_enabled = 1; \ EA = ea; / Reenable all other interrutps. / \ }

/ local functions / _inline RESPONSE_CATEGORY CheckCommStatusBytes(BYTE channel, BYTE pData, BYTE CommandNumber); _inline void BypassVariableScan(BYTE channel); _inline void CommFailure(void); _inline BYTE FindNextChannel(BYTE current_channel); _inline void revmemcpy(BYTE dst, BYTE src, WORD size); _inline void ScheduleChannelUpdate(BYTE channel); void UpdateChannelStatus(void); _inline void UpdatePassthru(void); _inline void UpdateDeviceInformation(BYTE channel); void ClearDeviceInfo (BYTE channel); void ManageChannels(BYTE recovering); RESPONSE_CATEGORY ParseResponse(BYTE channel, BYTE ptBuffer); RESPONSE_CATEGORY ParseResponse_0(BYTE channel, BYTE *ptBuffer);

ifdef FIX_CMD_2_PARSE_NOT_BEING_CALLED_ONTIME_2135

RESPONSE_CATEGORY ParseResponse_2(BYTE channel, BYTE *ptBuffer);

endif

RESPONSE_CATEGORY ParseResponse_3(BYTE channel, BYTE ptBuffer); RESPONSE_CATEGORY ParseResponse_6(BYTE channel, BYTE ptBuffer); RESPONSE_CATEGORY ParseResponse_9(BYTE channel, BYTE ptBuffer); RESPONSE_CATEGORY ParseResponse_12(BYTE channel, BYTE ptBuffer); RESPONSE_CATEGORY ParseResponse_13(BYTE channel, BYTE ptBuffer); RESPONSE_CATEGORY ParseResponse_14(BYTE channel, BYTE ptBuffer); RESPONSE_CATEGORY ParseResponse_15(BYTE channel, BYTE ptBuffer); RESPONSE_CATEGORY ParseResponse_16(BYTE channel, BYTE ptBuffer); RESPONSE_CATEGORY ParseResponse_38(BYTE channel, BYTE ptBuffer); RESPONSE_CATEGORY ParseResponse_48(BYTE channel, BYTE ptBuffer); RESPONSE_CATEGORY ParseResponse_50(BYTE channel, BYTE ptBuffer); RESPONSE_CATEGORY ParseResponse_59(BYTE channel, BYTE ptBuffer); RESPONSE_CATEGORY ParseResponse_109 (BYTE channel, BYTE ptBuffer); void ResetHartData(BYTE channel); BYTE SendHartCommand(BYTE channel, BYTE command_number, BYTE global_event); BYTE StripResponseHeader(BYTE rawBuffer, WORD ptr_header_size); void UnpackAscii(char pUnpacked, int nUnpackedByteCount, BYTE pPacked);

/* * HART Application State Machine State Functions / void HaSm_Init(BYTE this_channel, BYTE init); void HaSmWaitToSwitchChannel(BYTE this_channel, BYTE init); void HaSmWaitToConfigure(BYTE this_channel, BYTE init); void HaSmConnecting(BYTE this_channel, BYTE init); void HaSmSampling(BYTE this_channel, BYTE init); void HaSmServicePassthru(BYTE this_channel, BYTE init); void HaSmGetDeviceInformation(BYTE this_channel, BYTE init); void HaSmServiceGlobalEvent(BYTE this_channel, BYTE init);

ifdef IMPLEMENT_SLOT_CODES

void HaSmSamplingDeviceVar(BYTE this_channel, BYTE init); void ResetHartDeviceData(BYTE channel);

endif // #ifdef IMPLEMENT_SLOT_CODES

/ Per AB's Request Unblock all restricted commands for the "Logix" & "Connects" Passthrough interface / / to prevent conflicts with the DTM sending these Cmd's. / / These commands will still be blocked for the Ladder Pass-through Interface. / / Ladder Pass-through restricted HART commands: / / CMD# Description / / --- ------------- / / 59 HA_WRITE_NUMBER_RESP_PREAMBLE, / / 107 HA_WRITE_BUST_MODE_TVS, / / 108 HA_WRITE_BURST_MODE_CMD_NUMBER, / / 109 HA_BURST_MODE_CONTROL, / CONST BYTE ha_restricted_hart_commands[] = { 0x00, / CMD 00 through 07 / 0x00, / CMD 08 through 15 / 0x00, / CMD 16 through 23 / 0x00, / CMD 24 through 31 / 0x00, / CMD 32 through 39 / 0x00, / CMD 40 through 47 / 0x00, / CMD 48 through 55 / 0x08, / CMD 56 through 63 / 0x00, / CMD 64 through 71 / 0x00, / CMD 72 through 79 / 0x00, / CMD 80 through 87 / 0x00, / CMD 88 through 95 / 0x00, / CMD 96 through 103 / 0x38, / CMD 104 through 111 / 0x00, / CMD 112 through 119 / 0x00, / CMD 120 through 127 / 0x00, / CMD 128 through 135 / 0x00, / CMD 136 through 143 / 0x00, / CMD 144 through 151 / 0x00, / CMD 152 through 159 / 0x00, / CMD 160 through 167 / 0x00, / CMD 168 through 175 / 0x00, / CMD 176 through 183 / 0x00, / CMD 184 through 191 / 0x00, / CMD 192 through 199 / 0x00, / CMD 200 through 207 / 0x00, / CMD 208 through 215 / 0x00, / CMD 216 through 223 / 0x00, / CMD 224 through 231 / 0x00, / CMD 232 through 239 / 0x00, / CMD 240 through 247 / 0x00 / CMD 248 through 255 / };

/ Byte Masks to AND with: For determining bit set or reset (bitmasks[] from hwbase.c) / / to OR with: For setting a bit. /

/ Byte Masks to AND with: For clearing a bit / CONST BYTE bitmasks_clear[] = { 0xfe, / 1111 1110 ucm pending, channel 0 / 0xfd, / 1111 1101 ucm pending, channel 1 / 0xfb, / 1111 1011 ucm pending, channel 2 / 0xf7, / 1111 0111 ucm pending, channel 3 / 0xef, / 1110 1111 ucm pending, channel 4 / 0xdf, / 1101 1111 ucm pending, channel 5 / 0xbf, / 1011 1111 ucm pending, channel 6 / 0x7f / 0111 1111 ucm pending, channel 7 / };

/ Modem switching variables / / extern BYTE ADC_Channel; /

// Used during cfg or device info gathering to monitor for presence of 2nd master. BYTE secondary_master_rx_on_cfg[HA_MAX_CHANNEL];

/ The following variables are global HART state machine variables. / static BYTE hasm_loop_cnt; static BYTE ha_cold_start; / cold start / static BYTE ha_enabled_channels; / Which channels are enabled with HART functionality / static BYTE ha_sm_return_ok; / Do state calls within the state machine instead of nested calls. / static BYTE ha_next_input_channel; / Channel data to be stuffed in the input tags next go around / static BYTE ha_switching_channel; / Channel switching in progress? / static BYTE ha_channel; / current state machine channel / static BYTE ha_channel_next; / channel to be switched to / static BYTE ha_channel_prev; / last service channel / static BYTE ha_config_change; / Which channels have configuration change / static BYTE ha_full_scan_mask; / When a full scan is finished, the mask will be 0 / static BYTE ha_full_scan_mask_config; / Full scan mask as configured /

static BYTE ha_scan_counter; / When enough channels scanned to enable pass-through, the counter will be 0 /

//KPK 10/21/2002 - Eliminated this structure as an optimization. // static HA_CONTROL ha_Control[HA_MAX_CHANNEL]; / HART application state machine control structure / // Channel specific state machine variables: static PTR_SM_FUNCTION ha_ctrl_ptr_state_function[HA_MAX_CHANNEL]; / May be changed herein as described in HartAppStateMachine(). / static HART_APP_STATES ha_ctrl_state [HA_MAX_CHANNEL]; static BYTE ha_ctrl_configuration_set [HA_MAX_CHANNEL]; / Configuration received and valid / static BYTE ha_ctrl_hart_enabled [HA_MAX_CHANNEL]; / HART capability enabled / static BYTE ha_ctrl_hart_in_service [HA_MAX_CHANNEL]; / HART channel in normal service mode / static BYTE ha_ctrl_comm_established [HA_MAX_CHANNEL]; / Communication established, meaning unique address available. / static BYTE ha_ctrl_n_xmit_preamble_bytes [HA_MAX_CHANNEL]; / Number of preambles desired by the device. Defaults to 20. / static BYTE ha_ctrl_comm_failed [HA_MAX_CHANNEL]; / Derived from the communication process / static BYTE ha_ctrl_hart_comm_status [HA_MAX_CHANNEL]; / Extracted from the reponse PDU / static BYTE ha_ctrl_hart_device_status [HA_MAX_CHANNEL]; / Extracted from the reponse PDU / static BYTE ha_ctrl_hart_device_status_prev [HA_MAX_CHANNEL]; / Extracted from any reponse PDU including pass-through / static BYTE ha_ctrl_PDU_delimiter [HA_MAX_CHANNEL]; / Out module's delimiter / static BYTE ha_ctrl_next_poll_address [HA_MAX_CHANNEL]; / Next polling address / // BYTE ha_ctrl_device_address [HA_MAX_CHANNEL][5]; / 5-byte unique address / BYTE DeviceLongFormatAddress[HA_MAX_CHANNEL][5]; / 5-byte unique address / static BYTE ha_ctrl_restart_device_info [HA_MAX_CHANNEL]; / Event flag to trigger a new sequence of updating device information / static BYTE ha_ctrl_servicing_global_event [HA_MAX_CHANNEL]; / Flag to indicate special event in service / static BYTE ha_ctrl_more_status_avail [HA_MAX_CHANNEL]; / Flag to indicate more HART device status needed: issue CMD#48 / static BYTE ha_ctrl_reset_config_change [HA_MAX_CHANNEL]; / Flag to indicate reset config is needed: issue CMD#38 / static BYTE ha_ctrl_reset_bursting_device [HA_MAX_CHANNEL]; / Flag to indicate sending burst mode control is needed: issue CMD#109 / static BYTE ha_ctrl_hart_command [HA_MAX_CHANNEL]; / HART command number / static BYTE ha_ctrl_hart_command_sent [HA_MAX_CHANNEL]; / Indicate pending HART command's response is expected / / Supporting Device Information Messaging / static WORD ha_ctrl_dev_info_status [HA_MAX_CHANNEL];

//--- END HA_CONTROL --- static BYTE ha_evctrl_channel; / Channel number / static BYTE ha_evctrl_hart_command; / HART command number / static BYTE ha_evctrl_hart_command_sent; / Indicate pending HART command's response is expected / //--- End HA_EVENT_CONTROL ---

static BYTE ha_allow_hart_comm_fail[HA_MAX_CHANNEL];

static BYTE DeviceConnectedAtPollAddr0; // FIX_COMMFAILURE_ON_DEVICEINFO_UPDATE

/ Pass-thru variables / static UINT ha_handle_timeout; / 1..255 seconds / static BYTE ha_passthru_service_index; // ?? FOR NOW LEAVE THIS

static BYTE mPassThruNextHandle; / Next Available Handle Number / static BYTE mPassThruChannel; static BYTE mPassThruHandle; static T_PASS_THRU_QUEUE_TYPE_E mQueueType; static WORD mPassThruMsgSize; static BYTE mPassThruMsgBuffer[268];

static BYTE mHartChannelStatusByte[HA_MAX_CHANNEL]; static BYTE mConnectsDeviceDataChanged[HA_MAX_CHANNEL];

static REAL mHartDeviceReportedLoopCurrentMa[HA_MAX_CHANNEL];

static BYTE mHartDeviceIsRev5[HA_MAX_CHANNEL]; static BYTE mNumberValidCodesFromCmd50Reply[HA_MAX_CHANNEL]; static BYTE mInitialGatheringOfDeviceInformation[HA_MAX_CHANNEL]; static BYTE mHartCmd48ReplySize[HA_MAX_CHANNEL]; static BYTE mHartCmd9ScanCounter[HA_MAX_CHANNEL];

ifndef DEBUG_REPORT_CURRENT_DIFF_CHECK_DATA

static float mMScaleFactor0To20Ma; static float mBOffsetFactor0To20Ma;

else

float mMScaleFactor0To20Ma; float mBOffsetFactor0To20Ma;

endif

static T_HART_DYNAMIC_VARIABLES_S mHartDynamicVarsStruct[HA_MAX_CHANNEL];

ifdef NO_REPLY_DATA_ONTIME_1234

static BYTE mScanCmdMissedUpdateCounter[HA_MAX_CHANNEL];

endif

ifdef FIX_WRONG_ASSIGNMENT_CODES_USED_FOR_PVS_ONTIME_2387

static T_HART_PVs_ASSIGNMENT_CODES_S mPvSvTvFvAssignmentCodesForCmd9[HA_MAX_CHANNEL]; static BYTE mHartRev[HA_MAX_CHANNEL]; static BYTE mLastDeviceVarCode[HA_MAX_CHANNEL]; static BYTE mForceUseOfCmd3[HA_MAX_CHANNEL];

endif

/ Device Information Messaging variables / /* The HART I/O modules are required to automatically detect device configuration change. Device configuration change can be obtained by using the masking 0x40 with the device status. Device status is the 2nd status byte returned with each device response PDU. There are several control variables utilized in this file. They are: Variable Name Scope Description ----------------------------- ------ --------------------------------------------------- ha_pending_update_device_info Global Indicating device information update is in progress. Upon detection of device configuration change, a bit corresponding to the channel number will be set. It is cleared after the entire table of device info is updated. ha_dev_info_avail Global This is a 16-bit word showing the bit mask pattern when all commands associated with device information has been serviced. This bit pattern is determined once during initialization. ha_hart_dev_info[] Channel For each given channel, this is the device information for the connected HART field device. Information obtained from various HART commands are stored here. ha_ctrl_ha_ctrl_reset_config_change Channel Upon first detection of configuration change, the flag ha_ctrl_hart_device_status_prev .reset_config_change for a given channel will be set. reset_config_change will trigger global event to be serviced, thus sending a HART command #38 out to reset the device configuration change bit. Every time, a valid slave response if received, it is stored in the variable .hart_device_status_prev. Only a positive transition from 0 to 1 for bit 6 in the device status will cause the .reset_config_change to be set, and a new sequence of device information update be initiated. ha_ctrl_restart_device_info Channel It is possible that while device information is being updated, a device configuration change comes through. In this case, the sequence to obtain device information must be restarted. The channel control variable: ha_ctrl_restart_device_info flag which will trigger the * event for updating device information. / static BYTE ha_pending_update_device_info; / Which channels need device info update / static WORD ha_dev_info_avail; / bit pattern when all device information is available / static T_HART_DEVICE_INFORMATION_S HartDeviceInfoStruct[HA_MAX_CHANNEL];

/ Keeps Bit4 set after comm failure until re-connection & new device info / static BYTE ha_comm_failed_wait_for_new_device_info;

define HA_INSERVICE_SKIP_SECS 180 // 3 minutes

static BYTE ha_out_of_service_sec_cnts [HA_MAX_CHANNEL]; static BYTE ha_global_out_of_service;

static HA_CMD_48_DATA ha_cmd_48_data [HA_MAX_CHANNEL]; static BYTE cmd48_supported_flags[HA_MAX_CHANNEL]; static BYTE ha_cmd48_sec_cnts [HA_MAX_CHANNEL];

define CMD48_MAX_SKIP_SECS 120 // 2 minutes

static BYTE ha_broadcast_chan; static BYTE ha_channel_update_pending_cnt[HA_MAX_CHANNEL];

define MIN_CFG_CHG_BIT_SET_STABLE_CNTS 10

define MIN_CFG_BIT_CLEAR_PERIOD MS_5000

static short ha_cfg_chg_bit_clr_trigger_ticks0 [HA_MAX_CHANNEL]; static BYTE ha_cfg_chg_bit_clr_trigger [HA_MAX_CHANNEL]; static BYTE ha_cfg_chg_bit_set_cnts [HA_MAX_CHANNEL];

/*****

  • BOOLEAN HaClearCfgChngBitTrigger(BYTE device_status, BYTE channel)
  • Configuration changed bit clear trigger logic:
  • 1) We always want to process a "LO" to "HI" transition.
  • 2) Consider that the cfg chg bit can be stuck "HI" if the device is in write
  • protect mode. We want to process a change to "LO" if the device is
  • removed from write protect mode, but not falsely trigger on a delayed
  • status due to behavior of the clear cfg change bit command/response
  • when not write protected.
  • 3) Also consider a handheld may connect when write protected. Assume,
  • in this case the cfg changed. We have no way of knowing otherwise.
  • 4) During device commissioning via passthrough, multiple changes may be
  • be made that could set the cfg changed bit. Going offline to
  • reconfigure during commissioning could result in errors since module
  • may not be ready to respond.
  • Delay processing of the trigger MIN_CFG_BIT_CLEAR_PERIOD msec to
  • prevent multiple and uneeded processing and to prevent disruption of
  • device commissioning via passthrough interface. *****/ BOOLEAN HaClearCfgChngBitTrigger(BYTE device_status, BYTE channel) { short ticks_diff;

if (0 != (device_status & HPDU_CONFIGURATION_CHANGED)) // cfg chg bit = '1'? { if ((0 == ha_cfg_chg_bit_set_cnts[channel]) // 0 to 1 transition? || (secondary_master_rx_on_cfg [channel])) // OR secondary master present? { ha_cfg_chg_bit_clr_trigger [channel] = 1; // Trigger always. ha_cfg_chg_bit_clr_trigger_ticks0[channel] = GetTicks(); } if (ha_cfg_chg_bit_set_cnts[channel] < MIN_CFG_CHG_BIT_SET_STABLE_CNTS)// Prevent rollover. ha_cfg_chg_bit_set_cnts[channel]++; // Increment stability count. } else // cfg chg bit = '0'. { // Previously a stable '1'? if (ha_cfg_chg_bit_set_cnts[channel] >= MIN_CFG_CHG_BIT_SET_STABLE_CNTS) { ha_cfg_chg_bit_clr_trigger[channel] = 1; // Trigger if prev '1' was stable. ha_cfg_chg_bit_clr_trigger_ticks0[channel] = GetTicks(); } ha_cfg_chg_bit_set_cnts[channel] = 0; // Clear stablity count. }

// Prevent multiple module reconfigurations during commisioning // via passthrough commands or a handheld by delaying trigger. // Trigger delay will restart on multiple triggers and trigger // will not be acted upon until this delay expires. if (ha_cfg_chg_bit_clr_trigger[channel]) { secondary_master_rx_on_cfg[channel] = 0; // OK to clear. We're triggered. CalcTicksSince(ha_cfg_chg_bit_clr_trigger_ticks0[channel],ticks_diff); if (ticks_diff >= MIN_CFG_BIT_CLEAR_PERIOD) ha_cfg_chg_bit_clr_trigger[channel]++; // prevent missing this trigger. if (ha_cfg_chg_bit_clr_trigger[channel] >= 2) { ha_cfg_chg_bit_clr_trigger[channel] = 0; return 1; // Trigger clear cfg chg bit now. } // Else, wait to process the new trigger later when the module is not so busy. } return 0; // Do not trigger clear cfg chg bit now. }

// // _inline function bodies must be declared prior to referencing it in the file. //

/***

  • void revmemcpy(BYTE dst, BYTE src, WORD size) ***/ _inline void revmemcpy(BYTE dst, BYTE src, WORD size) { src = src + size - 1; for (; size > 0; size--) dst++ = src--; }

/***

  • void BypassVariableScan(BYTE channel)
  • This HA service clears the scanning mask(s) bit positions(s) to immitate
  • actual scanning of variables in case of communication failure, or other
  • non-active states. This is needed to enable pass through. For enhanced
  • pass-through, the function decrements the scan counter. ***/ _inline void BypassVariableScan(BYTE channel) { ha_ctrl_dev_info_status[channel] |= HA_DEV_INFO_CMD3; ha_full_scan_mask &= bitmasks_clear[channel]; }

/***

  • void UpdateDeviceInformation(BYTE channel)
  • This HA service clears currently stored device information and setup
  • global event mask to trigger new sequence of getting device information. ***/ _inline void UpdateDeviceInformation(BYTE channel) { / Update Device Information - Initiate a new sequence of device information gathering / / Clear up existing device information regardless of what's in it. / ClearDeviceInfo(channel);

/ Initiate a new sequence by wiping out the old information except for the address / if (ha_ctrl_state[channel] != HA_SM_CONNECTING) HaSmGetDeviceInformation(channel, SM_INITIALIZE); if (ha_pending_update_device_info & bitmasks[channel]) { / Already in the middle of getting device information, needs to restart the process. / ha_ctrl_restart_device_info[channel] = TRUE; } else ha_pending_update_device_info |= bitmasks[channel]; }

/***

  • void ScheduleChannelUpdate(BYTE channel) ***/ _inline void ScheduleChannelUpdate(BYTE channel) { if (ha_channel_update_pending_cnt[channel] < 0xFF)// Prevent rollunder. ha_channel_update_pending_cnt[channel]++; // Add a new request. }

/***

  • void UpdateChannelStatus(void)
  • Called every state machine cycle. Sends next pending channel update if
  • any. ***/ extern WORD BroadcastSequenceNumber; extern BYTE IncrementSequenceNumber; void UpdateChannelStatus(void) { BOOLEAN ea; BYTE index; BYTE *hip; static BYTE channel; static WORD sequence_number;

// Allow packet change? (Previous packet present for at least 1 RPI)? ea = EA; // save interrupt EA = 0; // disable interrupt if (sequence_number == BroadcastSequenceNumber) { EA = ea; // restore interrupt return; // Not held long enough. Hold this display. } else EA = ea; // restore interrupt

{ // Yes, see if we have any channels pending. for (index = 0; index < HA_MAX_CHANNEL; index++) // Check all n channels { if (++channel >= HA_MAX_CHANNEL) // Next channel channel = 0; if (ha_channel_update_pending_cnt[channel] > 0) // Requests pending? { goto update_channel_packet; } } return; // If we got here, no channels pending. }

update_channel_packet:

ifdef DEBUG_REPORT_CURRENT_DIFF_CHECK_DATA

if( channel !=0 ) { return; }

endif

if( ha_ctrl_hart_enabled[channel] ) { if ( (!ha_ctrl_comm_established[channel]) || ha_ctrl_comm_failed[channel] || (ha_pending_update_device_info & bitmasks[channel]) || (ha_comm_failed_wait_for_new_device_info & bitmasks[channel]) ) { mHartChannelStatusByte[channel] |= HART_CHAN_STATUSBITS_INITIALIZING; } else { mHartChannelStatusByte[channel] &= ~HART_CHAN_STATUSBITS_INITIALIZING; } }else{ mHartChannelStatusByte[channel] &= ~HART_CHAN_STATUSBITS_INITIALIZING; }

mHartDynamicVarsStruct[channel].HARTCommandStatus = ha_ctrl_hart_comm_status[channel]; mHartDynamicVarsStruct[channel].HARTFieldDeviceStatus = ha_ctrl_hart_device_status[channel];

/ Stuff HART data into the input tags / hip = (BYTE *)&plc_input_blk.HartInputStruct;

ea = EA; // save interrupt EA = 0; // disable interrupt IncrementSequenceNumber = TRUE; // Increment Sequence Number

memcpy((BYTE )&plc_input_blk.HartInputStruct.aHartVarsStruct[channel].HartPv, &mHartDynamicVarsStruct[channel].HartPvValue, 4(sizeof(REAL)) );

plc_input_blk.HartInputStruct.aHartChanStatusStruct[channel].StatusBits = mHartChannelStatusByte[channel];

ifdef NO_REPLY_DATA_ONTIME_1234

if( !ha_ctrl_hart_in_service[channel] || (mHartChannelStatusByte[channel] & HART_CHAN_STATUSBITS_FAIL) || (mHartDynamicVarsStruct[channel].HARTFieldDeviceStatus & 0x8D) || (mHartDynamicVarsStruct[channel].HartPvStatus == 0) )

else

if( !ha_ctrl_hart_in_service[channel] || (mHartChannelStatusByte[channel] & HART_CHAN_STATUSBITS_FAIL) || (mHartDynamicVarsStruct[channel].HARTFieldDeviceStatus & 0x8D) )

endif

{ plc_input_blk.module_status |= (MODS_CHAN_0_HART_FAULT << channel); }else{ plc_input_blk.module_status &= ~(MODS_CHAN_0_HART_FAULT << channel); }

plc_input_blk.HartInputStruct.aHartChanStatusStruct[channel].CommStatus = mHartDynamicVarsStruct[channel].HARTCommandStatus; plc_input_blk.HartInputStruct.aHartChanStatusStruct[channel].DeviceStatus = mHartDynamicVarsStruct[channel].HARTFieldDeviceStatus;

ifdef ADD_EXT_DEVICE_STATUS_IN_INPUT_BLOCK

plc_input_blk.HartInputStruct.aHartChanStatusStruct[channel].ExtDeviceStatus = mHartDynamicVarsStruct[channel].HartExtDeviceStatus;

endif

plc_input_blk.HartInputStruct.aHartVarsStruct[channel].HartPvStatus = mHartDynamicVarsStruct[channel].HartPvStatus; plc_input_blk.HartInputStruct.aHartVarsStruct[channel].HartSvStatus = mHartDynamicVarsStruct[channel].HartSvStatus; plc_input_blk.HartInputStruct.aHartVarsStruct[channel].HartTvStatus = mHartDynamicVarsStruct[channel].HartTvStatus; plc_input_blk.HartInputStruct.aHartVarsStruct[channel].HartFvStatus = mHartDynamicVarsStruct[channel].HartFvStatus;

// Timestamp for new packet data page: sequence_number = BroadcastSequenceNumber;

EA = ea; // Allow interrupts now. } /***

  • void CommFailure(void)
  • This HA service reset state machine variables to handle communiation faults. ***/ _inline void CommFailure(void) { ResetHartData(ha_channel);

/ Flag that channel has comm failure until re-connection & new device info / ha_comm_failed_wait_for_new_device_info |= bitmasks[ha_channel];

HaSmConnecting(ha_channel, SM_INITIALIZE); / Start over next time with new connection sequence / BypassVariableScan(ha_channel); ha_ctrl_comm_failed[ha_channel] = TRUE; ScheduleChannelUpdate(ha_channel); ManageChannels(SM_RECOVERING); / Let the HART channel manager decide what to do next? / }

/*****

  • void ScheduleCmd48 (BYTE channel) *****/ _inline void ScheduleCmd48 (BYTE channel) { ha_ctrl_more_status_avail[channel] = TRUE; HaSmServiceGlobalEvent(channel, SM_INITIALIZE); ha_evctrl_hart_command = HA_READ_ADDITIONAL_DEV_STATUS; ha_ctrl_servicing_global_event[channel] = TRUE; }

/*****

  • void CheckForExtendedStatus (BYTE channel) *****/ _inline void CheckForExtendedStatus (BYTE channel) { BYTE extnd_bits_now; ha_ctrl_more_status_avail[channel] = FALSE; // Default to not schedule command 48. if (!ha_ctrl_hart_in_service[channel] == TRUE) {// Out of service: // Make sure these bits show up as new if present when we return to service. ha_ctrl_hart_device_status[channel] &= ~(HPDU_MORE_STATUS_AVAIL | HPDU_DEVICE_MALFUNCTION); } else if ((cmd48_supported_flags[channel]) || (ha_ctrl_hart_device_status[channel] & HPDU_MORE_STATUS_AVAIL)) {// Responded to a command 48 previously OK OR Extended status flag is set: // Change in HPDU_MORE_STATUS_AVAIL OR HPDU_DEVICE_MALFUNCTION flags? extnd_bits_now = ha_ctrl_hart_device_status[channel] & (HPDU_MORE_STATUS_AVAIL | HPDU_DEVICE_MALFUNCTION); if (extnd_bits_now // An extended status bit changed? != (ha_ctrl_hart_device_status_prev[channel] & (HPDU_MORE_STATUS_AVAIL | HPDU_DEVICE_MALFUNCTION))) { if (extnd_bits_now == 0) // No need to read status if there is none. memset (&ha_cmd_48_data[channel], 0, HART_MAX_CMD_48_DATA); else ScheduleCmd48(channel); } // OR an extended status bit is set AND more than n seconds since last cmd48? else if ((extnd_bits_now) && (ha_cmd48_sec_cnts[channel] >= CMD48_MAX_SKIP_SECS)) ScheduleCmd48(channel); } }

/***

  • _inline RESPONSE_CATEGORY CheckCommStatusBytes(BYTE channel)
  • Call this routine to determine HART channel communication status.
  • The two Communication Status Bytes will be extracted and stored in their
  • corresponding local storage: ha_hart_comm_status[channel] and
  • ha_hart_device_status[channel. Overall HART channel health will be
  • determine and stored in ha_comm_established[channel] & ha_comm_failed[channel]
  • A good slave response would always have an echo of the master command in
  • the response PDU. If the command echo is invalid .hart_device_violation
  • will be set. This status will eventually be sent back to the host via the
  • input tags assembly.
  • ***/ _inline RESPONSE_CATEGORY CheckCommStatusBytes(BYTE channel, BYTE pData, BYTE CommandNumber) { / Possible return values are: RESPONSE_SUCCESS, COMM_ERROR, COMMAND_ERROR, COMMAND_NOT_SUPPORTED, RESPONSE_OTHER. / ha_ctrl_hart_comm_status[channel] = pData[HA_RSP_COMM_STATUS]; if (ha_ctrl_hart_comm_status[channel] & HPDU_COMM_ERR) { / Transmission, or reception error / ha_ctrl_comm_failed[channel] = TRUE; ha_ctrl_comm_established[channel] = FALSE; return COMM_ERROR; } else { / Received HART device response PDU without communication error. */ ha_ctrl_comm_failed [channel] = FALSE; ha_ctrl_comm_established[channel] = TRUE;

ha_allow_hart_comm_fail[channel] = 1; // Allow since comm established.

/ Determine device configuration change / ha_ctrl_hart_device_status[channel] = pData[HA_RSP_DEV_STATUS]; if (!ha_ctrl_hart_in_service[channel] == TRUE) { // No Device Configuration Change ha_ctrl_reset_config_change[channel] = FALSE; } else if (HaClearCfgChngBitTrigger(pData[HA_RSP_DEV_STATUS], channel)) { / Device Configuration changed / ha_ctrl_reset_config_change[channel] = TRUE; HaSmServiceGlobalEvent(channel, SM_INITIALIZE); ha_evctrl_hart_command = HA_RESET_CONFIGURATION_CHANGE_BIT; ha_ctrl_servicing_global_event[channel] = TRUE; UpdateDeviceInformation(channel); } else / No Device Configuration Change / ha_ctrl_reset_config_change[channel] = FALSE;

CheckForExtendedStatus(channel);

ha_ctrl_hart_device_status_prev[channel] = ha_ctrl_hart_device_status[channel];

/ Check command number in the response packet / if (CommandNumber != pData[HA_RSP_COMMAND_ECHO]) // Command echo must match { return COMMAND_ERROR; } else { // Response is for the proper HART command: check all common single-definition response codes. switch (ha_ctrl_hart_comm_status[channel]) { case H5_RC_000_SUCCESS: // Success and matching command echoed return RESPONSE_SUCCESS;

case H5_RC_064_CMD_NOT_IMPLEMENTED: return COMMAND_NOT_SUPPORTED;

case H5_RC_032_BUSY: default: // Other non-success response code. / Leave it for the state function to process it according to the command number / return RESPONSE_OTHER; } } } }

/***

  • _inline BYTE FindNextChannel(BYTE current_channel)
  • This function returns next enabled HART channel number if it exists. If
  • not, after looking at all available channels, this function returns the
  • current channel's value. ***/ _inline BYTE FindNextChannel(BYTE current_channel) { BYTE i; for (i = 0; i < HA_MAX_CHANNEL; i++) // Scan all channels once at most starting at next channel. { if (++current_channel == HA_MAX_CHANNEL) current_channel = 0; if ((ha_config_change) || (ha_ctrl_hart_enabled[current_channel])) { if (!ha_ctrl_hart_in_service[current_channel]) ha_sm_return_ok = 1; // Don't get stuck on an out of service channel. break; // Channel found that needs update. Exit loop. } } return current_channel; }

/***

  • _inline void UpdatePassthru(void)
  • Call this service to continue the handle timeout or invalidate handle
  • based on the timeout. ***/ _inline void UpdatePassthru(void) { int channel; BYTE* ChannelsNeedUpdateList;

ChannelsNeedUpdateList = HARTPASSTHRU_UpdateQueueTimers();

if( ChannelsNeedUpdateList != NULL ) { for(channel = 0; channel < 8; channel++) { if( ChannelsNeedUpdateList[channel] ) { / Reset Ladder PassThru Reply Ready Status Bit / mHartChannelStatusByte[channel] &= ~HART_CHAN_STATUSBITS_MSGREADY; ScheduleChannelUpdate(channel); } } } }

/***

  • void ClearDeviceInfo (BYTE channel)
  • Call this service to reset HART channel device information. This should
  • be called when new configuration is received or after HART communication
  • failed. ***/ void ClearDeviceInfo(BYTE channel) { secondary_master_rx_on_cfg[channel] = 0; // Start monitoring for 2nd master.

ha_ctrl_hart_device_status_prev [channel] = 0;

/ Clear all data in device information structure except for CMD 0 data if we are re-gathering device information / if( ha_ctrl_state[channel] == HA_SM_WAIT_TO_CONFIGURE || ha_ctrl_state[channel] == HA_SM_CONNECTING ) { ha_ctrl_dev_info_status[channel] = HA_DEV_INFO_NOT_AVAIL; memset(&HartDeviceInfoStruct[channel], 0, sizeof(T_HART_DEVICE_INFORMATION_S)); }else{ // Don't clear CMD 0 data and flag since we don't want to re-send CMD 0 during Device Information update ha_ctrl_dev_info_status[channel] = HA_DEV_INFO_CMD0;

ifdef ADD_NEW_16BIT_MFG_AND_DEVICE_TYPE_ONTIME_1491

/ Clear all data past CMD 0 data in structure AND Skip the new CMD 0 data placed after TagSize in V2.1 / / which start at offset 102 which is 18 bytes from the end. / / Then clear data from offset 104(PvLowerRangeValue) - 117 which is 14 bytes / / (WARNING: If structure definition changed for elements above TagSize then the subtract) / / ("16" value needs to be changed. ) / memset(&HartDeviceInfoStruct[channel].TagSize, 0, sizeof(T_HART_DEVICE_INFORMATION_S)-(16+18)); memset(&HartDeviceInfoStruct[channel].PvLowerRangeValue, 0, 14);

else

/ Clear all data past CMD 0 data in structure. / / (WARNING: If structure definition changed for elements above TagSize then the subtract) / / ("16" value needs to be changed. ) / memset(&HartDeviceInfoStruct[channel].TagSize, 0, sizeof(T_HART_DEVICE_INFORMATION_S)-16);

endif

}

if (BiteMode) { HartDeviceInfoStruct[channel].PvLowerRangeValue = HART_NaN; HartDeviceInfoStruct[channel].PvUpperRangeValue = HART_NaN; HartDeviceInfoStruct[channel].DampingValue = HART_NaN; } else { HartDeviceInfoStruct[channel].PvLowerRangeValue = 0.0; HartDeviceInfoStruct[channel].PvUpperRangeValue = 0.0; HartDeviceInfoStruct[channel].DampingValue = 0.0; } HartDeviceInfoStruct[channel].PvVariableCode = HART_INVALID_VAR_ASSIGN_CODE; HartDeviceInfoStruct[channel].SvVariableCode = HART_INVALID_VAR_ASSIGN_CODE; HartDeviceInfoStruct[channel].TvVariableCode = HART_INVALID_VAR_ASSIGN_CODE; HartDeviceInfoStruct[channel].FvVariableCode = HART_INVALID_VAR_ASSIGN_CODE; }

/***

  • void ResetHartData (BYTE channel)
  • Call this service to reset HART channel data. This should be called for
  • "channel modem/state machine reset" command is processed or when HART
  • communication failure is detected.
  • WARNING: Do not call this routine unless Device Information needs to be
  • be reset and the state function pointer is set for one of the
  • initial states: HaSmConnecting, HaSamWaitToConfigure, or
  • HaSmGetDeviceInformation. ***/ void ResetHartData(BYTE channel) { BYTE index;

ifdef ADD_NEW_ADDITIONAL_STATUS_BIT

int AllChannelsWithoutHart; int i; BYTE ea;

endif

ha_allow_hart_comm_fail[channel] = 1; // Allow since comm attempted but failed.

mHartDynamicVarsStruct[channel].HARTCommandStatus = ha_ctrl_hart_comm_status[channel]; mHartDynamicVarsStruct[channel].HARTFieldDeviceStatus = ha_ctrl_hart_device_status[channel];

/ Reset device information availability flag. / ha_ctrl_dev_info_status[channel] &= ~HA_DEV_INFO_CMD3;

/ Reset dynamic variable values and Primary Variable Analog Unit / if (BiteMode) { mHartDynamicVarsStruct[channel].HartPvValue = HART_NaN; mHartDynamicVarsStruct[channel].HartSvValue = HART_NaN; mHartDynamicVarsStruct[channel].HartTvValue = HART_NaN; mHartDynamicVarsStruct[channel].HartFvValue = HART_NaN; } else { mHartDynamicVarsStruct[channel].HartPvValue = 0.0; mHartDynamicVarsStruct[channel].HartSvValue = 0.0; mHartDynamicVarsStruct[channel].HartTvValue = 0.0; mHartDynamicVarsStruct[channel].HartFvValue = 0.0; mHartDynamicVarsStruct[channel].HartLoopCurrentValue = 0.0; }

mHartDynamicVarsStruct[channel].HartPvStatus = 0; mHartDynamicVarsStruct[channel].HartSvStatus = 0; mHartDynamicVarsStruct[channel].HartTvStatus = 0; mHartDynamicVarsStruct[channel].HartFvStatus = 0; mHartDynamicVarsStruct[channel].HartExtDeviceStatus = 0;

/ Reset the unit code in the Device Information Buffer / HartDeviceInfoStruct[channel].PvUnitsCode = HART_INVALID_UNIT_CODE; HartDeviceInfoStruct[channel].SvUnitsCode = HART_INVALID_UNIT_CODE; HartDeviceInfoStruct[channel].TvUnitsCode = HART_INVALID_UNIT_CODE; HartDeviceInfoStruct[channel].FvUnitsCode = HART_INVALID_UNIT_CODE;

mHartDynamicVarsStruct[channel].PvUnitsCode = HART_INVALID_UNIT_CODE; mHartDynamicVarsStruct[channel].SvUnitsCode = HART_INVALID_UNIT_CODE; mHartDynamicVarsStruct[channel].TvUnitsCode = HART_INVALID_UNIT_CODE; mHartDynamicVarsStruct[channel].FvUnitsCode = HART_INVALID_UNIT_CODE;

HartDeviceInfoStruct[channel].PvVariableCode = 0; HartDeviceInfoStruct[channel].SvVariableCode = 0; HartDeviceInfoStruct[channel].TvVariableCode = 0; HartDeviceInfoStruct[channel].FvVariableCode = 0; mHartDynamicVarsStruct[channel].PvVariableCode = 0; mHartDynamicVarsStruct[channel].SvVariableCode = 0; mHartDynamicVarsStruct[channel].TvVariableCode = 0; mHartDynamicVarsStruct[channel].FvVariableCode = 0;

mHartChannelStatusByte[channel] = DEFAULT_HART_CHANNEL_STATUS_BYTE_VALUE; mConnectsDeviceDataChanged[channel] = DEFAULT_DEVICE_DATA_CHANGED_VALUE;

mHartDeviceReportedLoopCurrentMa[channel] = 0.0;

mHartDeviceIsRev5[channel] = 1; mNumberValidCodesFromCmd50Reply[channel] = 0; mInitialGatheringOfDeviceInformation[channel] = 1; mHartCmd48ReplySize[channel] = 0; mHartCmd9ScanCounter[channel] = 0;

ifdef NO_REPLY_DATA_ONTIME_1234

mScanCmdMissedUpdateCounter[channel] = 0;

endif

ifdef ADD_NEW_ADDITIONAL_STATUS_BIT

AllChannelsWithoutHart = 1; // TBD IS THIS NECESSARY, MAYBE WE DON'T NEED TO CLEAR BIT for(i=0;i<NUM_ANALOG_CHANNELS;i++) { if( ha_ctrl_comm_established[i] ) { AllChannelsWithoutHart = 0; break; } } if( AllChannelsWithoutHart ) { ea = EA; EA = 0; plc_input_blk.module_status &= ~(MODS_NEW_ADDITIONAL_STATUS); EA = ea; }

endif

ifdef FIX_WRONG_ASSIGNMENT_CODES_USED_FOR_PVS_ONTIME_2387

mHartRev[channel] = 0; mLastDeviceVarCode[channel] = 0; mForceUseOfCmd3[channel] = 0;

endif

/* We might want to consider reset the scanning process when this happens. Other consideration should include: tossing pending pass-through or obtaining device information. The host could use this command to make the I/O module perform a "warm-start" thus causing all channels to be updated with latest * information without closing the connection or resending the configuration tags. / }

static BYTE ha_bite_state; static BYTE ha_bite_chan_under_test; static BYTE ha_bite_status; static float ha_bite_data[HA_MAX_CHANNEL];

/****

  • void HaBiteStateMachine (void)
  • UCM_HART_BITE_TEST:
  • Enter BITE mode by requesting BITE on any valid channel number. msg[0] = {0..7}.
  • Exit BITE mode by requesting BITE on channel number -1 (FFhex). msg[0] = -1.
  • msg[1] = 100
  • Expected usage:
  • 1) User configures system with no channels enabled for HART.
  • 2) User setups a channel for test.
  • 3) User requests a test on that channel.
  • 4) User polls for status.
  • 5) Repeat for all channels.
  • Send msg[0]: Channel: 0, 1, 2, 3, 4, 5, 6, 7 Exit BITE mode: FFhex -1dec
  • Send msg[1]: 100
  • Receive msg...[0]: Channel SINT: -1, 0, 1, 2, 3, 4, 5, 6, 7
  • Receive msg...[1]: Status SINT: BUSY(34) SUCCESS(0) DR_DEAD(35)
  • Receive msg[ 2.. 5]: channel 0 data REAL (1#QNaN if not read or faulted).
  • Receive msg[ 6.. 9]: channel 1 data REAL (1#QNaN if not read or faulted).
  • Receive msg[10..13]: channel 2 data REAL (1#QNaN if not read or faulted).
  • Receive msg[14..17]: channel 3 data REAL (1#QNaN if not read or faulted).
  • Receive msg[18..21]: channel 4 data REAL (1#QNaN if not read or faulted).
  • Receive msg[22..25]: channel 5 data REAL (1#QNaN if not read or faulted).
  • Receive msg[26..29]: channel 6 data REAL (1#QNaN if not read or faulted).
  • Receive msg[30..33]: channel 7 data REAL (1#QNaN if not read or faulted). ****/ void HaBiteStateMachine (void) { static short ticks0; short ticksdiff; RESPONSE_CATEGORY response_status; BYTE i;

switch(ha_bite_state) { case HAB_INIT_BITE: for (i = 0; i < HA_MAX_CHANNEL; i++) ha_bite_data[i] = HART_NaN; ha_bite_state = HAB_SEND_CMD0; // Fall through to next case

case HAB_SEND_CMD0: if (!RequestHartChannelSwitch(ha_bite_chan_under_test)) break; // Channel switch not granted. Try again later.

hu_channel = 0; // Always use channel 0 HART data structures.

//make sure Poll Address 0 is sent ha_ctrl_next_poll_address[hu_channel] = POLL_ADDRESS_0 | HPDU_MASTER_ADDRESS; addr_group_attempts[hu_channel] = 0;

SendHartCommand(hu_channel, HA_READ_UNIQUE_ID, SM_NORMAL); ticks0 = GetTicks(); ha_bite_state = HAB_WAIT_CMD0; break;

case HAB_WAIT_CMD0: if ((hu_state != HU_FAULTED) && (hu_rx_size != 0)) { response_status = ParseResponse(hu_channel, hu_rx_msg); if (RESPONSE_SUCCESS == response_status) ha_bite_state = HAB_SEND_CMD3; else { ha_bite_state = HAB_FAULTED; ha_bite_status = FAULTED; } } else { CalcTicksSince(ticks0,ticksdiff); if (ticksdiff >= HART_BITE_TIMEOUT) { ha_bite_state = HAB_FAULTED; ha_bite_status = FAULTED; } } break;

case HAB_SEND_CMD3: if (!RequestHartChannelSwitch(ha_bite_chan_under_test)) break; // Channel switch not granted. Try again later. hu_channel = 0; // Always use channel 0 HART data structures. SendHartCommand(hu_channel, HART_CMD_3_READ_DYNAMIC_VARS, SM_NORMAL); ticks0 = GetTicks(); ha_bite_state = HAB_WAIT_CMD3; break;

case HAB_WAIT_CMD3: if (hu_state == HU_FAULTED) ha_bite_state = HAB_FAULTED; else if (hu_rx_size != 0) { response_status = ParseResponse(hu_channel, hu_rx_msg); if (RESPONSE_SUCCESS == response_status) { ha_bite_data[ha_bite_chan_under_test] = mHartDynamicVarsStruct[hu_channel].HartPvValue; ha_bite_status = SUCCESS; ha_bite_state = HAB_IDLE; } else { ha_bite_state = HAB_FAULTED; ha_bite_status = FAULTED; } } else { CalcTicksSince(ticks0,ticksdiff); if (ticksdiff >= HART_BITE_TIMEOUT) { ha_bite_state = HAB_FAULTED; ha_bite_status = FAULTED; } } break;

case HAB_FAULTED: if (BiteMode) ha_bite_data[ha_bite_chan_under_test] = HART_NaN; else ha_bite_data[ha_bite_chan_under_test] = 0.0; ha_bite_status = DR_DEAD; break;

case HAB_IDLE: // Do nothing. Waiting for a new command. break;

case HAB_DISABLED: default: // Indeterminate state. Reinit HART and BITE. Will exit BITE mode. HA_InitializeApp(1); break; }; }

/***

  • void HA_InitializeApp(BYTE reconfigure)
  • Call this service only after cold start or new connection with a host.
  • This routine will reset everything as if the module is powering up from
  • a cold-start. ***/ void HA_InitializeApp(BYTE reconfigure) { BYTE index; BYTE ea; // Prevent UCM multiple thread conflict.

HartUcmDisable(); // Prevent UCM multiple thread conflict.

/ Initialize UART / HU_UartInit();

/ Initialize HART application / hasm_loop_cnt = 0; ha_cold_start = TRUE; ha_enabled_channels = 0; ha_next_input_channel = 0; / Start with channel 0 for PLC input / ha_config_change = 0; / No configuration changes / ha_pending_update_device_info = 0; ha_switching_channel = 0;

/ Clear comm failure until re-connection & new device info / ha_comm_failed_wait_for_new_device_info = 0;

memset(ha_out_of_service_sec_cnts, 0, HA_MAX_CHANNEL); ha_global_out_of_service = 0;

ha_broadcast_chan = 0xFF; memset(cmd48_supported_flags, 0, HA_MAX_CHANNEL); memset(ha_channel_update_pending_cnt, 1, HA_MAX_CHANNEL); memset(ha_cmd48_sec_cnts, 0, HA_MAX_CHANNEL);

memset(addr_group_attempts, 0, HA_MAX_CHANNEL);

memset(ha_cfg_chg_bit_clr_trigger, 0, HA_MAX_CHANNEL); memset(ha_cfg_chg_bit_set_cnts, 0, HA_MAX_CHANNEL);

/ Initialize global event control structure / HaSmServiceGlobalEvent(0, SM_INITIALIZE); ha_evctrl_channel = HU_NO_BURST_CHANS; ha_full_scan_mask_config = HART_ALL_CHANS_MASK; ha_full_scan_mask = ha_full_scan_mask_config;

PtSetScanCnt();

memset(&mHartChannelStatusByte[0],DEFAULT_HART_CHANNEL_STATUS_BYTE_VALUE,HA_MAX_CHANNEL); memset(&mConnectsDeviceDataChanged[0], DEFAULT_DEVICE_DATA_CHANGED_VALUE,HA_MAX_CHANNEL);

memset(&mHartDeviceIsRev5[0],1,sizeof(BYTE) HA_MAX_CHANNEL); memset(&mNumberValidCodesFromCmd50Reply[0],0,sizeof(BYTE) HA_MAX_CHANNEL); memset(&mInitialGatheringOfDeviceInformation[0],1,sizeof(BYTE) HA_MAX_CHANNEL); memset(&mHartCmd48ReplySize[0],0,sizeof(BYTE) HA_MAX_CHANNEL); memset(&mHartCmd9ScanCounter[0],0,sizeof(BYTE) * HA_MAX_CHANNEL);

ifdef FIX_WRONG_ASSIGNMENT_CODES_USED_FOR_PVS_ONTIME_2387

memset(&mHartRev[0],0,sizeof(BYTE) HA_MAX_CHANNEL); memset(&mLastDeviceVarCode[0],0,sizeof(BYTE) HA_MAX_CHANNEL); memset(&mForceUseOfCmd3[0],0,sizeof(BYTE) * HA_MAX_CHANNEL);

endif

/ AI module device information requires: Commands 0, 12, 13, 16, 50, and 3. / ha_dev_info_avail = (HA_DEV_INFO_CMD0 | HA_DEV_INFO_CMD12 | HA_DEV_INFO_CMD13 | HA_DEV_INFO_CMD15 | HA_DEV_INFO_CMD16 | HA_DEV_INFO_CMD48 | HA_DEV_INFO_CMD50 | HA_DEV_INFO_CMD38 | HA_DEV_INFO_CMD3);

// Passthrough variables HARTPASSTHRU_InitPassThruQueues(0); ha_passthru_service_index = PASSTHRU_QUEUE_SIZE; mPassThruNextHandle = 1; ha_handle_timeout = 0;

// Provide FTP Support ha_bite_chan_under_test = 0xFF; // Default to a non-existant channel so change will be sensed. ha_bite_state = HAB_DISABLED; ha_bite_status = DR_DEAD;

for (index = 0; index < HA_MAX_CHANNEL; index++) { if (BiteMode) ha_bite_data[index] = HART_NaN; else ha_bite_data[index] = 0.0; }

for (ha_channel = 0; ha_channel < HA_MAX_CHANNEL; ha_channel++) { / Initialize state machine control variables / HaSmWaitToConfigure(ha_channel, SM_INITIALIZE); ha_ctrl_configuration_set[ha_channel] = FALSE; }

/ Start off with channel 0 / ha_channel = 0; ha_channel_next = 0; ha_channel_prev = 0;

HaSmWaitToSwitchChannel(ha_channel, SM_INITIALIZE); ScheduleChannelUpdate(ha_channel);

if (TARGET == IF8H)

/ Calculate 0-20ma scaling factor / mMScaleFactor0To20Ma = DIV_FP(SUB_FP(eu_max_scale[RNG_0_20mA], eu_min_scale[RNG_0_20mA]), L2FP(adc_max_scale[RNG_0_20mA] - adc_min_scale[RNG_0_20mA])); / Y = MX + B B = Y - MX / mBOffsetFactor0To20Ma = SUB_FP(eu_max_scale[RNG_0_20mA], MUL_FP(mMScaleFactor0To20Ma, L2FP(adc_max_scale[RNG_0_20mA])));

elif (TARGET == OF8H)

/ Calculate 0-20ma scaling factor / mMScaleFactor0To20Ma = DIV_FP(SUB_FP(eu_max_scale[RNG_0_20mA], eu_min_scale[RNG_0_20mA]), L2FP(dac_max_scale[RNG_0_20mA] - dac_min_scale[RNG_0_20mA])); / Y = MX + B B = Y - MX / mBOffsetFactor0To20Ma = SUB_FP(eu_max_scale[RNG_0_20mA], MUL_FP(mMScaleFactor0To20Ma, L2FP(dac_max_scale[RNG_0_20mA])));

endif

HartUcmEnable(); // Prevent UCM multiple thread conflict.

DeviceConnectedAtPollAddr0 = 0;

if (reconfigure) HA_ConfigureApp(&mod_cfg_blk); }

/***

  • void HA_ConfigureApp (MOD_CFG_BLOCK *pcb)
  • This is the HART application service call from the Assembly Object after
  • receive the configuration tags. This call is asynchronous to the
  • execution of the HART state machine, thus requiring state functions to
  • check for event and take actions accordingly. ***/ void HA_ConfigureApp (MOD_CFG_BLOCK pcb) { BYTE i; BYTE index; CHAN_CFG_BLK pcc = NULL; BYTE ea; // Prevent UCM multiple thread conflict.

BYTE new_hart_enable;

HartUcmDisable(); // Prevent UCM multiple thread conflict.

ha_enabled_channels = 0; ha_full_scan_mask_config = HART_ALL_CHANS_MASK;

PtSetScanCnt();

/ Setup HART Passthrough Handle Timeout value / ha_handle_timeout = pcb->handle_timeout; HARTPASSTHRU_SetHandleTimeout(ha_handle_timeout);

if (ha_cold_start == TRUE) { ha_handle_timeout = pcb->handle_timeout; for (i = 0; i < HA_MAX_CHANNEL; i++) { pcc = &pcb->chan_cfg_blk[i]; if (pcc->cfg_bits & CCFGB_ENABLE_HART) / HART Enabled? / { / bit 7 for Input Module; bit 9 for Output Module / ha_ctrl_hart_enabled[i] = TRUE; ha_enabled_channels |= bitmasks[i]; } else { ha_ctrl_hart_enabled[i] = FALSE; ha_enabled_channels &= bitmasks_clear[i]; ha_full_scan_mask_config &= bitmasks_clear[i]; } ha_config_change = ha_config_change | bitmasks[i]; } ha_cold_start = FALSE; / Next time it won't be a cold start / goto config_exit; }

/ Must check for change of configuration / for (i = 0; i < HA_MAX_CHANNEL; i++) { pcc = &pcb->chan_cfg_blk[i];

if (pcc->cfg_bits & CCFGB_ENABLE_HART) / bit 9 / { new_hart_enable = TRUE; ha_enabled_channels |= bitmasks[i]; } else { ha_allow_hart_comm_fail[i] = 0; // Disallow since we aren't using this channel. new_hart_enable = FALSE; ha_enabled_channels &= bitmasks_clear[i]; ha_full_scan_mask_config &= bitmasks_clear[i]; } if (IsConnectionActive() == FALSE) / New connection? / { ha_ctrl_hart_in_service[i] = TRUE; / Reset service mode / } pcc = &pcb->chan_cfg_blk[i]; if (ha_ctrl_hart_enabled[i] != new_hart_enable) / Hart enable/disable changed / { // This code assums same scan rate of 1X for both dynamic and device variables. // When HART data scheduling becomes a requirement, this must be rewritten. HaSmWaitToConfigure(i, SM_INITIALIZE); ha_ctrl_hart_enabled[i] = new_hart_enable; ha_config_change = ha_config_change | bitmasks[i]; HARTPASSTHRU_ClearAllQueuesForChannel(i); } }

config_exit: ha_full_scan_mask = ha_full_scan_mask_config; PtSetScanCnt();

if (ha_enabled_channels == 0) ScheduleChannelUpdate(0);

HartUcmEnable(); // Prevent UCM multiple thread conflict. }

/****

  • void HaSuspendChannel (BYTE channel)
  • Take the indicated channel out of service. ****/ _inline void HaSuspendChannel (BYTE channel) { BYTE ea;

ha_ctrl_hart_in_service[channel] = FALSE;

/* Reset dynamic variable values to NANs. Do not call ResetHartData() because

  • it wipes out device information and indirectly causes ManageChannels() to
  • stay with the current state for the given channel. */ if (BiteMode) { mHartDynamicVarsStruct[channel].HartPvValue = HART_NaN; mHartDynamicVarsStruct[channel].HartSvValue = HART_NaN; mHartDynamicVarsStruct[channel].HartTvValue = HART_NaN; mHartDynamicVarsStruct[channel].HartFvValue = HART_NaN; } else { mHartDynamicVarsStruct[channel].HartPvValue = 0.0; mHartDynamicVarsStruct[channel].HartSvValue = 0.0; mHartDynamicVarsStruct[channel].HartTvValue = 0.0; mHartDynamicVarsStruct[channel].HartFvValue = 0.0;

mHartDynamicVarsStruct[channel].HartPvStatus = 0; mHartDynamicVarsStruct[channel].HartSvStatus = 0; mHartDynamicVarsStruct[channel].HartTvStatus = 0; mHartDynamicVarsStruct[channel].HartFvStatus = 0; }

ScheduleChannelUpdate(channel); }

/***

  • void HaResumeChannel (BYTE channel) ***/ _inline void HaResumeChannel (BYTE channel) { BYTE ea; ha_ctrl_hart_in_service[channel] = TRUE; ScheduleChannelUpdate(channel); }

int MODULECOMMAND_BiteServices(struct UCMESSAGE_STRUCT MsgStructPtr) { BYTE pRequestData; BYTE pReply; float pBiteData; BYTE Command, channel; int Status; BYTE ea; // Prevent multiple thread conflict. int i;

Status = OK; HartUcmDisable(); // Prevent multiple thread conflict.

pRequestData = (BYTE)MsgStructPtr->MSGDataPtr; pReply = (BYTE)MsgStructPtr->MSGReplyDataPtr;

Command = pRequestData[UCM_COMMAND]; channel = pRequestData[UCM_CHANNEL];

if( BiteMode ) { switch(Command) { case UCM_HART_BITE_TEST: { if (ha_bite_state == HAB_DISABLED) // Just entered BITE mode? { ha_bite_status = BUSY; ha_bite_state = HAB_INIT_BITE; // Then need init. }

if (channel < HA_MAX_CHANNEL) // Valid channel? { if (channel != ha_bite_chan_under_test) // New chan — You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/terryyin/lizard/issues/233#issuecomment-406747799, or mute the thread https://github.com/notifications/unsubscribe-auth/AAwJYrcZ37tGsoh1yBN2qPQUUw43eOxnks5uImJkgaJpZM4VXSW7.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHubhttps://github.com/terryyin/lizard/issues/233#issuecomment-406833467, or mute the threadhttps://github.com/notifications/unsubscribe-auth/AniQx00suqi_4FDj74UjdVAW0SQpRUb2ks5uI87IgaJpZM4VXSW7.