/// <summary> /// /// </summary> /// <param name="displayItem"></param> /// <returns></returns> override internal int TranslateDisplayAddress(SCSDisplayItem displayItem) { int nAddress; switch (displayItem.UpperAddress) { case 0x08: nAddress = (0x0100 | displayItem.LowerAddress); break; case 0x09: nAddress = (0x0200 | displayItem.LowerAddress); break; case 0x0A: nAddress = (0x0300 | displayItem.LowerAddress); break; case 0x0B: nAddress = (0x0400 | displayItem.LowerAddress); break; default: nAddress = displayItem.LowerAddress; break; } return(nAddress); }
/// <summary> /// This method returns a user viewable description for a given display item. NOte that /// all derived device classes must override this method. The base class implementation /// here is only provided as a debugging tool should a derived class not be able to /// successfully describe the given display item. /// </summary> /// <param name="displayItem">The display item </param> /// <returns>A string that describes the given display item /// </returns> /// <remarks > /// MM/DD/YY who Version Issue# Description /// -------- --- ------- ------ --------------------------------------- /// 12/07/06 mah 8.00.00 N/A Created /// </remarks> virtual internal string GetDisplayItemDescription(SCSDisplayItem displayItem) { String strDescription; // The following list are special case display items that are common to all SCS devices if (displayItem.RegisterClass == SCSDisplayItem.SCSDisplayClass.FixedBCD && displayItem.RegisterType == 0) { strDescription = "Segment Test"; } else if (displayItem.RegisterClass == SCSDisplayItem.SCSDisplayClass.DateValue && displayItem.LowerAddress == 0) { strDescription = "Current Date"; } else if (displayItem.RegisterClass == SCSDisplayItem.SCSDisplayClass.TimeValue && displayItem.LowerAddress == 0x06) { strDescription = "Current Time"; } else { strDescription = "Class = " + displayItem.RegisterClass.ToString() + " Type = " + displayItem.RegisterType.ToString(CultureInfo.InvariantCulture) + " @ 0x" + TranslateDisplayAddress(displayItem).ToString("X", CultureInfo.InvariantCulture); } return(strDescription); }
/// <summary> /// This method returns a user viewable description for a given display item. /// </summary> /// <param name="displayItem">The display item </param> /// <returns>A string that describes the given display item /// </returns> /// <remarks > /// MM/DD/YY who Version Issue# Description /// -------- --- ------- ------ --------------------------------------- /// 12/07/06 mah 8.00.00 N/A Created /// 03/09/07 mah 8.00.17 Added support for prs kW and prv kW even though they do not exist in a MT200 /// 03/26/07 mah 8.00.21 2769 Added support for time remaining in demand subinterval - again this is not supported /// by the 200 series but could be in the display list /// </remarks> override internal string GetDisplayItemDescription(SCSDisplayItem displayItem) { String strDescription = ""; if (displayItem.RegisterClass == SCSDisplayItem.SCSDisplayClass.DemandValue) { if (displayItem.RegisterType == 1) { strDescription = "prv kW"; } else { strDescription = "prs kW"; } } else if (displayItem.RegisterClass == SCSDisplayItem.SCSDisplayClass.TotalContinuousCumulativeValue) { if (displayItem.RegisterType == 0x07) { strDescription = "Last Season "; } strDescription += "ccum " + DemandQuantity; // Add the TOU rate indeicator switch (displayItem.TOURate) { case 1: strDescription += " Rate A"; break; case 2: strDescription += " Rate B"; break; case 3: strDescription += " Rate C"; break; case 4: strDescription += " Rate D"; break; default: break; } } else if (displayItem.RegisterClass == SCSDisplayItem.SCSDisplayClass.TimeValue && 0x48 == displayItem.LowerAddress) // Time remaining in sub interval { strDescription = "Time Rem in Demand Subint"; } else { strDescription = GetDisplayItemDescription(TranslateDisplayAddress(displayItem)); } if (strDescription.Length == 0) { strDescription = base.GetDisplayItemDescription(displayItem); } return(strDescription); }
/// <summary> /// This method is responsible for either retrieving or calculating the continuous /// cummulative demand value associated with the given display item. Note /// that in the CENTRON this value is simply read from the meter. /// </summary> /// <param name="displayItem">The display item to look up</param> /// <returns>A string representing the ccum value /// </returns> /// <remarks > /// MM/DD/YY who Version Issue# Description /// -------- --- ------- ------ --------------------------------------- /// 01/03/07 mah 8.00.00 N/A Created /// </remarks> override internal String RetrieveCCumValue(SCSDisplayItem displayItem) { String strCCum = ""; if (displayItem.RegisterType == 0) // current season { strCCum = ReadFloatingBCDValue(TranslateDisplayAddress(displayItem), 4); } else if (displayItem.RegisterType == 7) // last season { strCCum = ReadFloatingBCDValue(TranslateDisplayAddress(displayItem), 3); } return(strCCum); }
/// <summary> /// This method is responsible for retrieving the basepage address of any given /// meter display item - normal, alternate, or test mode /// </summary> /// <param name="displayItem">The display item to look up</param> /// <returns>An integer representing the associated basepage address /// </returns> /// <remarks > /// MM/DD/YY who Version Issue# Description /// -------- --- ------- ------ --------------------------------------- /// 12/07/06 mah 8.00.00 N/A Created /// 03/26/07 mah 8.00.21 2769 Added support for time remaining in demand subinterval - this is not supported /// by the 200 series but could be in the display list /// </remarks> override internal int TranslateDisplayAddress(SCSDisplayItem displayItem) { int nAddress; if (displayItem.RegisterClass == SCSDisplayItem.SCSDisplayClass.InstantaneousValue) { nAddress = 0; // Instantaneous values apply to CENTRONs only - don't try to retrieve them } else if (displayItem.RegisterClass == SCSDisplayItem.SCSDisplayClass.TimeValue && 0x48 == displayItem.LowerAddress) // Time remaining in sub interval { nAddress = 0x00; // This is a CENTRON only item - don't try to retrieve it either } else { switch (displayItem.UpperAddress) { case 0x01: if (displayItem.LowerAddress == 0x0C) { nAddress = 0x00FF; // Current day of week } else { nAddress = displayItem.LowerAddress; } break; case 0x08: nAddress = (0x0100 | displayItem.LowerAddress); break; case 0x09: nAddress = (0x0200 | displayItem.LowerAddress); break; case 0x0A: nAddress = (0x0300 | displayItem.LowerAddress); break; case 0x0B: nAddress = (0x0400 | displayItem.LowerAddress); break; default: nAddress = displayItem.LowerAddress; break; } } return(nAddress); }
/// <summary> /// This method is responsible for either retrieving or calculating the continuous /// cummulative demand value associated with the given display item. Note /// that this method is not currently implemented and will throw an exception /// </summary> /// <param name="displayItem">The display item to look up</param> /// <returns>A string representing the ccum value /// </returns> /// <remarks > /// MM/DD/YY who Version Issue# Description /// -------- --- ------- ------ --------------------------------------- /// 01/03/07 mah 8.00.00 N/A Created /// </remarks> override internal String RetrieveCCumValue(SCSDisplayItem displayItem) { throw (new NotImplementedException("Continuous cumulative display items are yet not implemented for this device")); }
/// <summary> /// This method is responsible for retrieving the basepage address of any given /// meter display item - normal, alternate, or test mode /// </summary> /// <param name="displayItem"></param> /// <returns></returns> /// <remarks > /// MM/DD/YY who Version Issue# Description /// -------- --- ------- ------ --------------------------------------- /// 12/07/06 mah 8.00.00 N/A Created /// </remarks> override internal int TranslateDisplayAddress(SCSDisplayItem displayItem) { int nAddress = 0; // Handle present and previous demands separately if (displayItem.RegisterClass == SCSDisplayItem.SCSDisplayClass.EnergyValue && displayItem.DisplayType == DisplayMode.TEST_MODE) { nAddress = 0x0119; } else if (displayItem.RegisterClass == SCSDisplayItem.SCSDisplayClass.MaxDemandValue && displayItem.DisplayType == DisplayMode.TEST_MODE) { nAddress = 0x0124; } else if (displayItem.RegisterClass == SCSDisplayItem.SCSDisplayClass.DemandValue) { if (displayItem.RegisterType == 0) // present demand { nAddress = 0x0561; } else // Previous demand { nAddress = 0x0565; } } else if (displayItem.RegisterClass == SCSDisplayItem.SCSDisplayClass.TotalContinuousCumulativeValue) { if (displayItem.RegisterType == 0) // current season { switch (displayItem.TOURate) { case SCSDisplayItem.DISP_RATE_E: nAddress = 0x558; break; case SCSDisplayItem.DISP_RATE_A: nAddress = 0x533; break; case SCSDisplayItem.DISP_RATE_B: nAddress = 0x537; break; case SCSDisplayItem.DISP_RATE_C: nAddress = 0x53B; break; case SCSDisplayItem.DISP_RATE_D: nAddress = 0x53F; break; } } else // last season { switch (displayItem.TOURate) { case SCSDisplayItem.DISP_RATE_E: nAddress = 0x55C; break; case SCSDisplayItem.DISP_RATE_A: nAddress = 0x543; break; case SCSDisplayItem.DISP_RATE_B: nAddress = 0x546; break; case SCSDisplayItem.DISP_RATE_C: nAddress = 0x549; break; case SCSDisplayItem.DISP_RATE_D: nAddress = 0x54C; break; } } } else if (0x0C == displayItem.UpperAddress) { nAddress = (0x0500 | displayItem.LowerAddress); } // If we were unable to determine the address at this point, the display item // must represent an item that is common to both the MT200 and the CENTRON // registers - call the base class to get its address if (0 == nAddress) { nAddress = base.TranslateDisplayAddress(displayItem); } return(nAddress); }
/// <summary> /// This method is responsible for calculating the continuous /// cummulative demand value associated with the given display item. Note /// that the MT200 does not store this value so it must be calculated manually /// </summary> /// <param name="displayItem">The display item to look up</param> /// <returns>A string representing the ccum value /// </returns> /// <remarks > /// MM/DD/YY who Version Issue# Description /// -------- --- ------- ------ --------------------------------------- /// 01/03/07 mah 8.00.00 N/A Created /// 03/07/07 mah 8.00.17 Implemented the method /// </remarks> override internal String RetrieveCCumValue(SCSDisplayItem displayItem) { // We need to calculate the ccum value ourselves and return it. int nBCDLength; double dblCumDemand = 0.0; double dblMaxDemand = 0.0; int nCumDemandRegisterAddress; int nMaxDemandRegisterAddress; // Before reading the registers we have to take note of what type of register we are looking up // because last season registers are shorter than the current season registers - that's in addition // to being stored in completely different basepage addresses if (displayItem.RegisterType == 0x00) { switch (displayItem.TOURate) { case 1: nMaxDemandRegisterAddress = (int)MT2Addresses.RATEA_KW; nCumDemandRegisterAddress = (int)MT2Addresses.RATEA_CUM_KW; break; case 2: nMaxDemandRegisterAddress = (int)MT2Addresses.RATEB_KW; nCumDemandRegisterAddress = (int)MT2Addresses.RATEB_CUM_KW; break; case 3: nMaxDemandRegisterAddress = (int)MT2Addresses.RATEC_KW; nCumDemandRegisterAddress = (int)MT2Addresses.RATEC_CUM_KW; break; case 4: nMaxDemandRegisterAddress = (int)MT2Addresses.RATED_KW; nCumDemandRegisterAddress = (int)MT2Addresses.RATED_CUM_KW; break; default: nMaxDemandRegisterAddress = (int)MT2Addresses.RATEE_KW; nCumDemandRegisterAddress = (int)MT2Addresses.RATEE_CUM_KW; break; } nBCDLength = 4; // All current registers are 4 bytes long } else { switch (displayItem.TOURate) { case 1: nMaxDemandRegisterAddress = (int)MT2Addresses.LAST_SEASON_RATEA_KW; nCumDemandRegisterAddress = (int)MT2Addresses.LAST_SEASON_CUM_RATEA; break; case 2: nMaxDemandRegisterAddress = (int)MT2Addresses.LAST_SEASON_RATEB_KW; nCumDemandRegisterAddress = (int)MT2Addresses.LAST_SEASON_CUM_RATEB; break; case 3: nMaxDemandRegisterAddress = (int)MT2Addresses.LAST_SEASON_RATEC_KW; nCumDemandRegisterAddress = (int)MT2Addresses.LAST_SEASON_CUM_RATEC; break; case 4: nMaxDemandRegisterAddress = (int)MT2Addresses.LAST_SEASON_RATED_KW; nCumDemandRegisterAddress = (int)MT2Addresses.LAST_SEASON_CUM_RATED; break; default: nMaxDemandRegisterAddress = (int)MT2Addresses.LAST_SEASON_RATEE_KW; nCumDemandRegisterAddress = (int)MT2Addresses.LAST_SEASON_CUM_RATEE; break; } nBCDLength = 3; // All last season registers are 3 bytes long } // Now that we have the addresses of both the cummulative demand register and the maximum demand // register we can calculate continuous cummulative demand if (nCumDemandRegisterAddress != 0) { dblCumDemand = double.Parse(ReadFloatingBCDValue(nCumDemandRegisterAddress, nBCDLength), CultureInfo.CurrentCulture); } if (nMaxDemandRegisterAddress != 0) { dblMaxDemand = double.Parse(ReadFloatingBCDValue(nMaxDemandRegisterAddress, nBCDLength), CultureInfo.CurrentCulture); } double dblCCumDemand = dblCumDemand + dblMaxDemand; return(dblCCumDemand.ToString(CultureInfo.CurrentCulture)); }
/// <summary> /// The MT200 does not have any previous demand values - however the CENTRON does and it /// is possible to program an MT200 with a CENTRON's display list. This method must be overriden /// to prevent attempts to read memory locations that simply are not present in the 200. /// </summary> /// <returns></returns> /// <remarks > /// MM/DD/YY who Version Issue# Description /// -------- --- ------- ------ --------------------------------------- /// 03/09/07 mah 8.00.00 N/A Created /// </remarks> override internal string ReadPreviousDemandValue(SCSDisplayItem displayItem) { return(""); }
/// <summary> /// Returns a list of values currently shown on the meter's normal mode display /// </summary> /// <remarks > /// MM/DD/YY who Version Issue# Description /// -------- --- ------- ------ --------------------------------------- /// 12/05/06 mah 8.00.00 N/A Created /// </remarks> virtual protected void ReadDisplayConfigurations() { byte[] byDisplayItem; SCSProtocolResponse ProtocolResponse; m_NormalDisplayList = new List <DisplayItem>(); m_AlternateDisplayList = new List <DisplayItem>(); m_TestDisplayList = new List <DisplayItem>(); // All SCS devices MUST have at least one item in the normal display // list. Read each of the items in the list until we reach either an end // of table indicator or we find the first item in the alternate display list int nDisplayItemAddress = DisplayTableAddress; Boolean boolEndOfList = false; m_Logger.WriteLine(Logger.LoggingLevel.Detailed, "Reading display table"); while (!boolEndOfList) { // Read the entire display item from the meter ProtocolResponse = m_SCSProtocol.Upload(nDisplayItemAddress, SCS_DISPLAYITEM_LENGTH, out byDisplayItem); if (SCSProtocolResponse.SCS_ACK == ProtocolResponse) { SCSDisplayItem displayItem = CreateDisplayItem(ref byDisplayItem, 0, false); if (!displayItem.EndOfFIle) { if (displayItem.DisplayType == ItronDevice.DisplayMode.NORMAL_MODE) { m_NormalDisplayList.Add(displayItem); } else { m_AlternateDisplayList.Add(displayItem); } // set the address of the next item nDisplayItemAddress += SCS_DISPLAYITEM_LENGTH; } else { boolEndOfList = true; nDisplayItemAddress += 1; // The end of list is only one byte long - reset the address to point to the next item } } else { SCSException scsException = new SCSException(SCSCommands.SCS_U, ProtocolResponse, nDisplayItemAddress, "Display table item"); throw scsException; } } // The test mode list immediately follows the normal and alternate mode lists and has the same // format boolEndOfList = false; m_Logger.WriteLine(Logger.LoggingLevel.Detailed, "Reading test mode display table"); while (!boolEndOfList) { // Read the entire display item from the meter ProtocolResponse = m_SCSProtocol.Upload(nDisplayItemAddress, SCS_DISPLAYITEM_LENGTH, out byDisplayItem); if (SCSProtocolResponse.SCS_ACK == ProtocolResponse) { SCSDisplayItem displayItem = new SCSDisplayItem(ref byDisplayItem, 0, true); if (!displayItem.EndOfFIle) { m_TestDisplayList.Add(displayItem); // set the address of the next item nDisplayItemAddress += SCS_DISPLAYITEM_LENGTH; } else { boolEndOfList = true; } } else { SCSException scsException = new SCSException(SCSCommands.SCS_U, ProtocolResponse, nDisplayItemAddress, "Test Mode display item"); throw scsException; } } }
/// <summary> /// /// </summary> /// <returns></returns> /// <remarks > /// MM/DD/YY who Version Issue# Description /// -------- --- ------- ------ --------------------------------------- /// 12/13/06 mah 8.00.00 N/A Created /// </remarks> virtual internal string ReadPreviousDemandValue(SCSDisplayItem displayItem) { return(ReadFloatingBCDValue(TranslateDisplayAddress(displayItem), 4)); }
/// <summary> /// This method is responsible for either retrieving or calculating the continuous /// cummulative demand value associated with the given display item. Note /// that in some SCS devices this value is simply read from the meter yet in /// other cases, the value must be calculated from the cummulative and max /// demand values. Since the actual implementation differs per device type, this /// method must be overriden for each derived device type /// </summary> /// <param name="displayItem">The display item to look up</param> /// <returns>A string representing the ccum value /// </returns> /// <remarks > /// MM/DD/YY who Version Issue# Description /// -------- --- ------- ------ --------------------------------------- /// 01/03/07 mah 8.00.00 N/A Created /// </remarks> abstract internal String RetrieveCCumValue(SCSDisplayItem displayItem);
/// <summary> /// This method is responsible for retrieving the basepage address of any given /// meter display item - normal, alternate, or test mode. SInce all basepage addresses /// are unique per meter type, this method is a pure abstract method. Each /// derived meter class will have its own implementation /// </summary> /// <param name="displayItem">The display item to look up</param> /// <returns>An integer representing the associated basepage address /// </returns> /// <remarks > /// MM/DD/YY who Version Issue# Description /// -------- --- ------- ------ --------------------------------------- /// 12/07/06 mah 8.00.00 N/A Created /// </remarks> abstract internal int TranslateDisplayAddress(SCSDisplayItem displayItem);