From 87c8fa0f0c92b61825973f44ba19686564fdd0cd Mon Sep 17 00:00:00 2001 From: Yuta Doi Date: Fri, 8 Jun 2018 22:32:39 +0900 Subject: PolicyManager manage layout information (area, category, role) Change-Id: I0841819ef73a20308223414bca8d74f5b26215f5 Signed-off-by: Yuta Doi --- src/policy_manager/policy_manager.cpp | 376 ++++++++++++++++++++++++---------- src/policy_manager/zipc/dummy_stm.c | 5 +- src/policy_manager/zipc/dummy_stm.h | 40 +++- 3 files changed, 305 insertions(+), 116 deletions(-) diff --git a/src/policy_manager/policy_manager.cpp b/src/policy_manager/policy_manager.cpp index 73f9350..2429cf2 100644 --- a/src/policy_manager/policy_manager.cpp +++ b/src/policy_manager/policy_manager.cpp @@ -33,17 +33,33 @@ extern "C" { namespace pm { -typedef std::unordered_map AppAttribute; -typedef std::unordered_map AreasState; -typedef std::unordered_map LayoutState; -typedef std::unordered_map LayersState; +typedef struct AreaState { + std::string name; + std::string category; + std::string role; +} AreaState; + +typedef struct LayoutState { + std::string name; + std::map category_num; +// int category_num[stm::gStmCategoryNoNum]; + std::vector area_list; +} LayoutState; + +typedef struct LayerState { + std::string name; + LayoutState layout_state; +} LayerState; struct sd_event* event_loop; std::map event_source_list; +std::map g_event_info_list; PolicyManager::CallbackTable callback; -LayersState g_prv_layers; -LayersState g_crr_layers; -LayoutState g_default_layout_state; + +std::unordered_map g_prv_layers; +std::unordered_map g_crr_layers; +std::unordered_map g_prv_layers_car_stop; +std::unordered_map g_default_layouts; } // namespace pm @@ -69,7 +85,7 @@ int PolicyManager::initialize() { this->eventname2no_[stm::gStmEventName[i]] = stm::gStmEventNo[i]; } - for (unsigned int i=0; icategoryname2no_[stm::gStmCategoryName[i]] = stm::gStmCategoryNo[i]; } @@ -94,17 +110,19 @@ int PolicyManager::initialize() { } // Initialize current/previous state of layers - pm::AppAttribute init_app; - pm::AreasState init_area; + pm::AreaState init_area; pm::LayoutState init_layout; - init_app["role"] = "none"; - init_area["none"] = init_app; - init_layout["none"] = init_area; - - pm::g_crr_layers["homescreen"] = init_layout; - pm::g_crr_layers["apps"] = init_layout; - pm::g_crr_layers["restriction"] = init_layout; - pm::g_crr_layers["on_screen"] = init_layout; + init_area.name = "none"; + init_area.category = "none"; + init_area.role = "none"; + init_layout.area_list.push_back(init_area); + + for (int i = stm::gStmLayerNoMin; i <= stm::gStmLayerNoMax; i++) { + const char* layer_name = stm::gStmLayerName[i]; + pm::g_crr_layers[layer_name].name = layer_name; + pm::g_crr_layers[layer_name].layout_state = init_layout; + } + pm::g_prv_layers = pm::g_crr_layers; // Initialize StateTransitioner @@ -155,25 +173,25 @@ static void addStateToJson( json_object_object_add(*json_out, key, json_obj); } -static int checkPolicyEntry(int event, uint64_t delay_ms); +static int checkPolicyEntry(int event, uint64_t delay_ms, const char* role); static int checkPolicy(sd_event_source *source, void *data) { HMI_DEBUG("wm:pm", "Call"); HMI_DEBUG("wm:pm", ">>>>>>>>>> START CHECK POLICY"); - int event = *((int*)data); + int event_data = *((int*)data); int event_no, category_no, area_no; - event_no = event & STM_MSK_EVT_NO; - category_no = event & STM_MSK_CTG_NO; - area_no = event & STM_MSK_ARA_NO; + event_no = (event_data & STM_MSK_EVT_NO) - 1; + category_no = ((event_data & STM_MSK_CTG_NO) >> 8) - 1; + area_no = ((event_data & STM_MSK_ARA_NO) >> 16) - 1; HMI_DEBUG("wm:pm", ">>>>>>>>>> event:%s category:%s area:%s", - stm::gStmEventName[event_no - 1], - stm::gStmCategoryName[(category_no >> 8) - 1], - stm::gStmAreaName[(area_no >> 16) - 1]); + stm::gStmEventName[event_no], + stm::gStmCategoryName[category_no], + stm::gStmAreaName[area_no]); // Transition state stm::stm_state_t crr_state; - int ret = stm::stmTransitionState(event, &crr_state); + int ret = stm::stmTransitionState(event_data, &crr_state); if (0 > ret) { HMI_ERROR("wm:pm", "Error!!"); return -1; @@ -220,53 +238,98 @@ static int checkPolicy(sd_event_source *source, void *data) { crr_state.layer[stm::gStmLayerNoOnScreen].state, stm::gStmLayoutNo2Name[crr_state.layer[stm::gStmLayerNoOnScreen].state]); -#if 0 // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if 1 // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ // Store previous layers pm::g_prv_layers = pm::g_crr_layers; - std::string layer_name = "homescreen"; - - // changed? - if (crr_state.layer[stm::gStmLayerNoHomescreen].is_changed) { - // Get previous layout name of this layer - pm::LayoutState prv_layout_state = pm::g_prv_layers[layer_name]; - std::string prv_layout_name = prv_homescreen_layout_state.first(); - - // Get current layout name of this layer - std::string crr_layout_name = std::string(stm::gStmLayoutNo2Name[crr_state.layer[stm::gStmLayerNoHomescreen].state]); - - // Compare layout name - pm::LayoutState crr_layout_state; - if ("none" == crr_layout_name) { - // If current layout is "none", - // current areas is set with "none" - HMI_DEBUG("wm:pm", "Current layout is \"none\""); - HMI_DEBUG("wm:lm", "Current layout is \"none\""); - pm::AppAttribute crr_app_attribute; - pm::AreasState crr_areas_state; - crr_app_attribute["role"] = "none"; - crr_areas_state["none"] = "none"; - crr_layout_state["none"] = crr_areas_state; + std::string role = pm::g_event_info_list[event_data]; + std::string event = std::string(stm::gStmEventName[event_no]); + std::string category = std::string(stm::gStmCategoryName[category_no]); + std::string area = std::string(stm::gStmAreaName[area_no]); + + for (int layer_no = stm::gStmLayerNoMin; + layer_no <= stm::gStmLayerNoMax; layer_no++) { + const char* layer_name = stm::gStmLayerName[layer_no]; + HMI_DEBUG("wm:pm", "LAYER:%s", layer_name); + +#if 1 + // If restriction mode is changed off -> on, + // store current state for state of restriction mode off + if ((crr_state.restriction_mode.is_changed) + && (stm::gStmRestrictionModeStateNoOn == crr_state.restriction_mode.state)) { + HMI_DEBUG("wm:lm", "Store current state for state of restriction mode off"); + pm::g_prv_layers_car_stop[layer_name] = pm::g_crr_layers[layer_name]; + } +#else + // If car state is changed car_stop -> car_run, + // store current state for state of car stop + if ((crr_state.car.is_changed) + && (stm::gStmCarStateNoRun == crr_state.car.state)) { + HMI_DEBUG("wm:lm", "Store current state for state of car stop"); + pm::g_prv_layers_car_stop[layer_name] = pm::g_crr_layers[layer_name]; } - else { - if (prv_layout_name == crr_layout_name) { - // If previous layout is same with current, - // previous areas are copied to current - crr_layout_state[crr_layout_name] = pm::g_prv_layers[layer_name][crr_layout_name]; +#endif + + + // This layer is changed? + if (crr_state.layer[layer_no].is_changed) { + // Get previous layout name of this layer + pm::LayoutState prv_layout_state = pm::g_prv_layers[layer_name].layout_state; + std::string prv_layout_name = prv_layout_state.name; + + // Get current layout name of this layer + int crr_layout_state_no = crr_state.layer[layer_no].state; + std::string crr_layout_name = std::string(stm::gStmLayoutNo2Name[crr_layout_state_no]); + + pm::LayoutState crr_layout_state; +#if 1 + if ((crr_state.restriction_mode.is_changed) + && (stm::gStmRestrictionModeStateNoOff == crr_state.restriction_mode.state)) { + // If restriction mode is changed on -> off, + // restore state of restriction mode off + HMI_DEBUG("wm:lm", "Restriction mode is changed on -> off, so restore state of restriction mode off"); + crr_layout_state = pm::g_prv_layers_car_stop[layer_name].layout_state; +#else + if ((crr_state.car.is_changed) + && (stm::gStmCarStateNoStop == crr_state.car.state)) { + // If car state is changed car_run -> car_stop, + // restore state of car stop + HMI_DEBUG("wm:lm", "Car state is changed car_run -> car_stop, so restore state of car stop"); + crr_layout_state = pm::g_prv_layers_car_stop[layer_name].layout_state; +#endif } - else { - // If previous layout is same with current, - // previous areas are copied to current - crr_layout_state[crr_layout_name] = this->default_layout_state[crr_layout_name]; + else if ("none" == crr_layout_name) { + // If current layout is "none", + // current areas is set with "none" + HMI_DEBUG("wm:pm", "Current layout is \"none\""); + pm::AreaState crr_area_state; + crr_area_state.name = "none"; + crr_area_state.category = "none"; + crr_area_state.role = "none"; + crr_layout_state.name = "none"; + crr_layout_state.area_list.push_back(crr_area_state); } + else { + if (prv_layout_name == crr_layout_name) { + // If previous layout is same with current, + // previous areas are copied to current + HMI_DEBUG("wm:lm", "Previous layout is same with current"); + crr_layout_state = prv_layout_state; + } + else { + // If previous layout is NOT same with current, + // current areas is set with default value + HMI_DEBUG("wm:lm", "Previous layout is NOT same with current"); + crr_layout_state = pm::g_default_layouts[crr_layout_name]; + } - // Update role in new area + // Update role in new area #if 1 - if (crr_state.restriction_mode.is_changed) { - // Updating role is not necessary - // because new_role is not specified - // when restriction mode is changed - HMI_DEBUG("wm:lm", "Updating role is not necessary because new_role is not specified when restriction mode is changed"); + if (crr_state.restriction_mode.is_changed) { + // Updating role is not necessary + // because new_role is not specified + // when restriction mode is changed + HMI_DEBUG("wm:lm", "Updating role is not necessary because new_role is not specified when restriction mode is changed"); #else if (crr_state.car.is_changed) { // Updating role is not necessary @@ -276,36 +339,113 @@ static int checkPolicy(sd_event_source *source, void *data) { #endif } else { - HMI_DEBUG("wm:lm", "Get new_area for new role"); - // Get new_area for new role - std::string new_area = this->getAreaName(this->layout_define_[crr_layout_name], - new_role, category); - if ("none" == new_area) { - HMI_DEBUG("wm:lm", "It is not necessary to update role of areas in this layer, because new_role is not specified for this layer"); + std::map> ctg_list; + for (int ctg_no=stm::gStmCategoryNoMin; + ctg_no<=stm::gStmCategoryNoMax; ctg_no++) { + const char* ctg = stm::gStmCategoryName[ctg_no]; + HMI_DEBUG("wm:pm", "ctg:%s", ctg); + + // Create area list of previous/current app + std::vector area_list; + for (pm::AreaState area_state : prv_layout_state.area_list) { + if (std::string(ctg) == area_state.category) { + HMI_DEBUG("wm:pm", "push area_state to prv list"); + area_list.push_back(area_state); + } + } + + int prv_ctg_num = prv_layout_state.category_num[ctg]; + int crr_ctg_num = crr_layout_state.category_num[ctg]; + HMI_DEBUG("wm:pm", "crr_ctg_num:%d prv_ctg_num:%d", crr_ctg_num, prv_ctg_num); + + if (crr_ctg_num < prv_ctg_num) { + HMI_DEBUG("wm:pm", "crr_ctg_num < prv_ctg_num"); + if (0 == strcmp(ctg, category.c_str())) { + HMI_DEBUG("wm:pm", "category of requested role is same"); + for (auto i = area_list.begin(); i != area_list.end(); ++i) { + if ((role == i->role) && (area == i->name)) { + HMI_DEBUG("wm:pm", "remove requested role data"); + area_list.erase(i); + } + } + } + else { + HMI_DEBUG("wm:pm", "remove the oldest area data"); + area_list.erase(area_list.begin()); + } + } + else if (crr_ctg_num >= prv_ctg_num) { + HMI_DEBUG("wm:pm", "crr_ctg_num >= prv_ctg_num"); + if (0 == strcmp(ctg, category.c_str())) { + HMI_DEBUG("wm:pm", "category of requested role is same"); + if ((crr_ctg_num == prv_ctg_num) && (0 != prv_ctg_num)) { + HMI_DEBUG("wm:pm", "remove the oldest area data for adding requested role"); + area_list.erase(area_list.begin()); + } + } + } + + // Check + for (pm::AreaState area_state : prv_layout_state.area_list) { + HMI_DEBUG("wm:pm", "area_list name:%s category:%s role:%s", + area_state.name.c_str(), + area_state.category.c_str(), + area_state.role.c_str()); + } + + ctg_list[ctg] = area_list; } - else { - // Is there new_area? - // if there is new_area, set new role there - // if NOT, find same category of new_role - // pop old role and shift area - // push new role and set area + if (event == "activate") { + // First, update area for requested role + bool updated = false; + for (pm::AreaState &as : crr_layout_state.area_list) { + if ((as.category == category) && (as.name == area)) { + as.role = role; + updated = true; + break; + } + } + + // If NOT updated: there is not requested area in new layout, + // requested role is used later. + if (!updated) { + pm::AreaState area_state; + area_state.name = area; + area_state.category = category; + area_state.role = role; + ctg_list[category].push_back(area_state); + } + } + // Update areas + for (pm::AreaState &as : crr_layout_state.area_list) { - // Update role in new area - // because new_role is specified for this layer - TypeRolCtg crr_role; - crr_role["role"] = std::string(new_role); - crr_layout[crr_layout_name][new_area] = crr_role; + // This conditional expression is useful in only activate + if (as.role != role) { + as.role = ctg_list[as.category].begin()->role; + ctg_list[as.category].erase(ctg_list[as.category].begin()); + } } } } + // Update current layout of this layer + pm::g_crr_layers[layer_name].layout_state = crr_layout_state; } + } - // Update current layout of this layer - pm::g_crr_layers[layer_name] = crr_layout_state; - + // Check + for (auto itr : pm::g_crr_layers) { + pm::LayerState ls = itr.second; + HMI_DEBUG("wm:pm", ">>> LAYER:%s",ls.name.c_str()); + HMI_DEBUG("wm:pm", ">>> >>> LAYOUT:%s", ls.layout_state.name.c_str()); + + for (pm::AreaState as : ls.layout_state.area_list) { + HMI_DEBUG("wm:pm", ">>> >>> >>> AREA:%s", as.name.c_str()); + HMI_DEBUG("wm:pm", ">>> >>> >>> >>> CTG:%s", as.category.c_str()); + HMI_DEBUG("wm:pm", ">>> >>> >>> >>> ROLE:%s", as.role.c_str()); + } } #endif // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@ -436,11 +576,11 @@ static int checkPolicy(sd_event_source *source, void *data) { if (crr_state.car.is_changed) { if (stm::gStmCarStateNoRun == crr_state.car.state) { // Set delay event(restriction mode on) - checkPolicyEntry(STM_EVT_NO_RESTRICTION_MODE_ON, 3000); + checkPolicyEntry(STM_EVT_NO_RESTRICTION_MODE_ON, 3000, nullptr); } else if (stm::gStmCarStateNoStop == crr_state.car.state) { // Set event(restriction mode off) - checkPolicyEntry(STM_EVT_NO_RESTRICTION_MODE_OFF, 0); + checkPolicyEntry(STM_EVT_NO_RESTRICTION_MODE_OFF, 0, nullptr); // Stop timer for restriction on event if (pm::event_source_list.find(STM_EVT_NO_RESTRICTION_MODE_ON) @@ -466,8 +606,8 @@ static int checkPolicy(sd_event_source *source, void *data) { sd_event_source_unref(source); // Remove event source from list - if (pm::event_source_list.find(event) != pm::event_source_list.end()) { - pm::event_source_list.erase(event); + if (pm::event_source_list.find(event_data) != pm::event_source_list.end()) { + pm::event_source_list.erase(event_data); } HMI_DEBUG("wm:pm", ">>>>>>>>>> FINISH CHECK POLICY"); @@ -475,19 +615,29 @@ static int checkPolicy(sd_event_source *source, void *data) { } static int timerEvent(sd_event_source *source, uint64_t usec, void *data) { - checkPolicy(source, data); + int ret = checkPolicy(source, data); + return ret; }; -static int checkPolicyEntry(int event, uint64_t delay_ms) +static int checkPolicyEntry(int event, uint64_t delay_ms, const char* role) { HMI_DEBUG("wm:pm", "Call"); HMI_DEBUG("wm:pm", "event:0x%x", event); + // Store event info + if (nullptr == role) { + pm::g_event_info_list[event] = std::string(""); + } + else { + pm::g_event_info_list[event] = std::string(role); + } + if (0 == delay_ms) { int ret = sd_event_add_defer(pm::event_loop, NULL, &checkPolicy, new int(event)); if (0 > ret) { HMI_ERROR("wm:pm", "Faild to sd_event_add_defer: errno:%d", ret); + pm::g_event_info_list.erase(event); return -1; } } @@ -507,6 +657,7 @@ static int checkPolicyEntry(int event, uint64_t delay_ms) &timerEvent, new int(event)); if (0 > ret) { HMI_ERROR("wm:pm", "Faild to sd_event_add_time: errno:%d", ret); + pm::g_event_info_list.erase(event); return -1; } @@ -569,7 +720,7 @@ int PolicyManager::inputEvent(json_object* json_in) { } // Check policy - checkPolicyEntry((event_no | category_no | area_no), 0); + checkPolicyEntry((event_no | category_no | area_no), 0, role); return 0; } @@ -730,7 +881,15 @@ int PolicyManager::loadLayoutDb() { HMI_DEBUG("wm:pm", "json_area_array len:%d", len_area); HMI_DEBUG("wm:pm", "json_area_array dump:%s", json_object_get_string(json_area_array)); - pm::AreasState areas_state; + pm::LayoutState layout_state; + pm::AreaState area_state; + std::map category_num; + for (int ctg_no = stm::gStmCategoryNoMin; + ctg_no <= stm::gStmCategoryNoMax; ctg_no++) { + const char* ctg_name = stm::gStmCategoryName[ctg_no]; + category_num[ctg_name] = 0; + } + for (int j=0; j> area:%s", area); // Get app attribute of the area - pm::AppAttribute app_attribute; category = this->getStringFromJson(json_area, "category"); if (nullptr == category) { HMI_ERROR("wm:pm", "Parse Error!!"); return -1; } - app_attribute["category"] = std::string(category); + area_state.category = std::string(category); + category_num[category]++; HMI_DEBUG("wm:pm", ">>> category:%s", category); role = this->getStringFromJson(json_area, "role"); if (nullptr != role) { // Role is NOT essential here - app_attribute["role"] = std::string(role); + area_state.role = std::string(role); HMI_DEBUG("wm:pm", ">>> role:%s", role); } - areas_state[area] = app_attribute; + layout_state.area_list.push_back(area_state); + } - pm::g_default_layout_state[layout] = areas_state; + layout_state.name = layout; + layout_state.category_num = category_num; + pm::g_default_layouts[layout] = layout_state; } // Check - for(auto itr_layout = pm::g_default_layout_state.begin(); - itr_layout != pm::g_default_layout_state.end(); ++itr_layout) { + for(auto itr_layout = pm::g_default_layouts.begin(); + itr_layout != pm::g_default_layouts.end(); ++itr_layout) { HMI_DEBUG("wm:pm", ">>> layout:%s", itr_layout->first.c_str()); - for (auto itr_area = itr_layout->second.begin(); - itr_area != itr_layout->second.end(); ++itr_area) { - HMI_DEBUG("wm:pm", ">>> >>> area:%s", itr_area->first.c_str()); - + for (auto itr_area = itr_layout->second.area_list.begin(); + itr_area != itr_layout->second.area_list.end(); ++itr_area) { + HMI_DEBUG("wm:pm", ">>> >>> area :%s", itr_area->name.c_str()); + HMI_DEBUG("wm:pm", ">>> >>> category:%s", itr_area->category.c_str()); + HMI_DEBUG("wm:pm", ">>> >>> role :%s", itr_area->role.c_str()); +#if 0 for (auto itr_role = itr_area->second.begin(); itr_role != itr_area->second.end(); ++itr_role) { HMI_DEBUG("wm:pm", ">>> >>> >>> attribute:%s, name:%s", itr_role->first.c_str(), itr_role->second.c_str()); } +#endif } } diff --git a/src/policy_manager/zipc/dummy_stm.c b/src/policy_manager/zipc/dummy_stm.c index 4d8b057..a53d28f 100644 --- a/src/policy_manager/zipc/dummy_stm.c +++ b/src/policy_manager/zipc/dummy_stm.c @@ -39,6 +39,7 @@ const int gStmEventNo[] = { }; const char* gStmCategoryName[] = { + "none", "homescreen", "map", "general", @@ -49,6 +50,7 @@ const char* gStmCategoryName[] = { }; const int gStmCategoryNo[] = { + STM_CTG_NO_NONE, STM_CTG_NO_HOMESCREEN, STM_CTG_NO_MAP, STM_CTG_NO_GENERAL, @@ -132,7 +134,8 @@ const char* gStmLayoutNo2Name[] = { "restriction.split.sub", }; -const char* gStmLayerNo2Name[] = { +const char* gStmLayerName[] = { + "none", "homescreen", "apps", "restriction", diff --git a/src/policy_manager/zipc/dummy_stm.h b/src/policy_manager/zipc/dummy_stm.h index c0011f3..c94e8e0 100644 --- a/src/policy_manager/zipc/dummy_stm.h +++ b/src/policy_manager/zipc/dummy_stm.h @@ -41,13 +41,14 @@ #define STM_EVT_NO_RESTRICTION_MODE_ON 0x0F // Category number -#define STM_CTG_NO_HOMESCREEN 0x0100 -#define STM_CTG_NO_MAP 0x0200 -#define STM_CTG_NO_GENERAL 0x0300 -#define STM_CTG_NO_SPLITABLE 0x0400 -#define STM_CTG_NO_POPUP 0x0500 -#define STM_CTG_NO_SYSTEM_ALERT 0x0600 -#define STM_CTG_NO_RESTRICTION 0x0700 +#define STM_CTG_NO_NONE 0x0100 +#define STM_CTG_NO_HOMESCREEN 0x0200 +#define STM_CTG_NO_MAP 0x0300 +#define STM_CTG_NO_GENERAL 0x0400 +#define STM_CTG_NO_SPLITABLE 0x0500 +#define STM_CTG_NO_POPUP 0x0600 +#define STM_CTG_NO_SYSTEM_ALERT 0x0700 +#define STM_CTG_NO_RESTRICTION 0x0800 // Area number #define STM_ARA_NO_FULL 0x010000 @@ -66,7 +67,7 @@ // Number of events, categories and areas #define STM_NUM_EVT 15 -#define STM_NUM_CTG 7 +//#define STM_NUM_CTG 7 #define STM_NUM_ARA 8 // Enum for state @@ -121,13 +122,33 @@ enum stm_layout_ { gStmLayoutNoRestrictionSplitSub, }; +enum stm_category_ { + gStmCategoryNoNone = 0, + gStmCategoryNoHomescreen, + gStmCategoryNoMap, + gStmCategoryNoGeneral, + gStmCategoryNoSplitable, + gStmCategoryNoPopup, + gStmCategoryNoSystemAlert, + gStmCategoryNoRestriction, + + gStmCategoryNoNum, + + gStmCategoryNoMin = gStmCategoryNoNone, + gStmCategoryNoMax = gStmCategoryNoNum - 1, +}; + enum stm_layer_ { - gStmLayerNoHomescreen = 0, + gStmLayerNoNone = 0, + gStmLayerNoHomescreen, gStmLayerNoApps, gStmLayerNoRestriction, gStmLayerNoOnScreen, gStmLayerNoNum, + + gStmLayerNoMin = gStmLayerNoNone, + gStmLayerNoMax = gStmLayerNoNum - 1, }; extern const char* gStmEventName[]; @@ -137,7 +158,6 @@ extern const int gStmCategoryNo[]; extern const char* gStmAreaName[]; extern const int gStmAreaNo[]; extern const char* gStmLayerName[]; -extern const int gStmLayerNo[]; // String for state extern const char* gStmParkingBrakeStateNo2Name[]; -- cgit 1.2.3-korg