From 1f8f92fa6804cab305575e11922e60ff346e80be Mon Sep 17 00:00:00 2001
From: gscalamera <graziano.scalamera@elettra.eu>
Date: Fri, 6 Dec 2024 09:16:57 +0100
Subject: [PATCH] Support 2 indexes for 2-dimensional arrays (Image)

---
 docs/Formula.md       |  13 ++-
 src/AlarmHandler.cpp  | 184 ++++++++++++++++++++++++++++++++----------
 src/AlarmHandler.h    |   2 +-
 src/alarm_table.h     |  47 +++++++----
 src/event_table.cpp   |   6 +-
 src/event_table.h     |   7 +-
 src/formula_grammar.h |   4 +-
 7 files changed, 198 insertions(+), 65 deletions(-)

diff --git a/docs/Formula.md b/docs/Formula.md
index 69515cf..d731f51 100644
--- a/docs/Formula.md
+++ b/docs/Formula.md
@@ -99,9 +99,18 @@ The read part of every attribute is internally extracted in a vector of double w
 
 # Operation on arrays
 
-## Index to access single element
+## Indexes to access elements
 
-A single element of an array attribute (Spectrum, Image) can be extracted with a single index between square brackets (e.g. ```name/of/dev/attr[ind]```). Since Image attributes are extracted to vectors as well, the single index can access every element as long as the size of rows is known. Accessing an *Image* element using two indexes for the two dimensions is not supported at the moment.
+- A single element of a one-dimensional array attribute (Spectrum) can be extracted with a single index between square brackets (e.g. ```name/of/dev/attr[ind]```).
+- A single element of a two-dimensional array attribute (Image) can be extracted with two indexes between square brackets (e.g. ```name/of/dev/attr[ind_row][ind_column]```).
+- A one-dimensional array can be extracted from a two-dimensional array attribute (Image) with a single index between square brackets (e.g. ```name/of/dev/attr[ind_row]```).
+
+If any index exceeds actual dimensions of the array, the formula is evaluated to ERROR with an out of bounds exception.
+
+## Limitations
+
+- It is not possible to specify array constants (e.g. ```name/of/dev/attr > [val1,val2]```).
+- It is not possible to use indexes on expressions (e.g. ```(any_expression)[ind]```).
 
 ## Unary operators and functions
 
diff --git a/src/AlarmHandler.cpp b/src/AlarmHandler.cpp
index 927e4da..8b50b96 100644
--- a/src/AlarmHandler.cpp
+++ b/src/AlarmHandler.cpp
@@ -3863,6 +3863,9 @@ void AlarmHandler::do_alarm(bei_t& e)
 	{
 		found->value = e.value;
 		found->value_string = e.value_string;
+		found->read_size = e.read_size;
+		found->dim_x = e.dim_x;
+		found->dim_y = e.dim_y;
 		found->quality = e.quality;
 		//found->errors = e.errors;
 		found->ex_reason = e.ex_reason;
@@ -4525,7 +4528,7 @@ formula_res_t AlarmHandler::eval_formula(tree_parse_info_t tree, string &attr_va
     return res;
 }
 
-formula_res_t AlarmHandler::eval_expression(iter_t const& i, string &attr_values, int ev_ind) //throw (string &), std::out_of_range
+formula_res_t AlarmHandler::eval_expression(iter_t const& i, string &attr_values, vector<double> ev_ind)
 {
 
     ostringstream err;
@@ -4702,56 +4705,65 @@ formula_res_t AlarmHandler::eval_expression(iter_t const& i, string &attr_values
 		DEBUG_STREAM << "		node event" << string(i->value.begin(), i->value.end()) << endl;
 #endif
 		formula_res_t ind;
-		if(i->children.size() != 2)		
+		if(i->children.size() < 2)
 		{
         	err <<  "in node event_ID(" << string(i->value.begin(), i->value.end()) << ") children=" << i->children.size();;
         	throw err.str(); 
         }
-		if((i->children.begin()+1)->value.id() == formula_grammar::indexID)
-			ind = eval_expression(i->children.begin()+1, attr_values);		//array index
-		else if((i->children.begin()+1)->value.id() == formula_grammar::propertyID)
+		size_t child_ind=0;
+		while((child_ind < i->children.size() -1) && ((i->children.begin()+child_ind+1)->value.id() == formula_grammar::indexID))
+		{
+			formula_res_t ind_tmp;
+			ind_tmp = eval_expression(i->children.begin()+child_ind+1, attr_values);		//array index
+			if(ind_tmp.value.size() == 1 && ind_tmp.value[0]>=0)
+			{
+				ind.value.push_back(ind_tmp.value[0]);
+			}
+			child_ind++;
+		}
+		if(child_ind+1 == i->children.size() -1  && (i->children.begin()+child_ind+1)->value.id() == formula_grammar::propertyID)
 		{
-			if(string((i->children.begin()+1)->value.begin(), (i->children.begin()+1)->value.end()) == ".quality")
+			if(string((i->children.begin()+child_ind+1)->value.begin(), (i->children.begin()+child_ind+1)->value.end()) == ".quality")
 			{
 				formula_res_t res;
 				res = eval_expression(i->children.begin(), attr_values);
 				res.value = {(double)res.quality};
 				ostringstream temp_attr_val;
-				temp_attr_val << "\"" <<  res.attr_name << string((i->children.begin()+1)->value.begin(), (i->children.begin()+1)->value.end()) << "\":[" <<print_vector(res.value) << "],";
+				temp_attr_val << "\"" <<  res.attr_name << string((i->children.begin()+child_ind+1)->value.begin(), (i->children.begin()+child_ind+1)->value.end()) << "\":[" <<print_vector(res.value) << "],";
 				attr_values += temp_attr_val.str();
 #ifdef _DEBUG_FORMULA
 				DEBUG_STREAM << "		node event.quality -> " << print_vector(res.value) << endl;
 #endif
 				return res;
 			}
-			else if(string((i->children.begin()+1)->value.begin(), (i->children.begin()+1)->value.end()) == ".alarm")
+			else if(string((i->children.begin()+child_ind+1)->value.begin(), (i->children.begin()+child_ind+1)->value.end()) == ".alarm")
 			{
 				formula_res_t res;
 				if(!ind.value.empty())
-					res = eval_expression(i->children.begin(), attr_values, (int)ind.value[0]);
+					res = eval_expression(i->children.begin(), attr_values, ind.value);
 				else
 					res = eval_expression(i->children.begin(), attr_values);
 				std::transform(res.value.begin(), res.value.end(), res.value.begin(),
 								[](double n) { return (double)((n == _UNACK) || (n == _ACKED)); });
 				ostringstream temp_attr_val;
-				temp_attr_val << "\"" <<  res.attr_name << string((i->children.begin()+1)->value.begin(), (i->children.begin()+1)->value.end()) << "\":[" <<print_vector(res.value) << "],";
+				temp_attr_val << "\"" <<  res.attr_name << string((i->children.begin()+child_ind+1)->value.begin(), (i->children.begin()+child_ind+1)->value.end()) << "\":[" <<print_vector(res.value) << "],";
 				attr_values += temp_attr_val.str();
 #ifdef _DEBUG_FORMULA
 				DEBUG_STREAM << "		node event.alarm -> " << print_vector(res.value)<< endl;
 #endif
 				return res;
 			}
-			else if(string((i->children.begin()+1)->value.begin(), (i->children.begin()+1)->value.end()) == ".normal")
+			else if(string((i->children.begin()+child_ind+1)->value.begin(), (i->children.begin()+child_ind+1)->value.end()) == ".normal")
 			{
 				formula_res_t res;
 				if(!ind.value.empty())
-					res = eval_expression(i->children.begin(), attr_values, (int)ind.value[0]);
+					res = eval_expression(i->children.begin(), attr_values, ind.value);
 				else
 					res = eval_expression(i->children.begin(), attr_values);
 				std::transform(res.value.begin(), res.value.end(), res.value.begin(),
 								[](double n) { return (double)((n == _NORM) || (n == _RTNUN)); });
 				ostringstream temp_attr_val;
-				temp_attr_val << "\"" <<  res.attr_name << string((i->children.begin()+1)->value.begin(), (i->children.begin()+1)->value.end()) << "\":[" <<print_vector(res.value) << "],";
+				temp_attr_val << "\"" <<  res.attr_name << string((i->children.begin()+child_ind+1)->value.begin(), (i->children.begin()+child_ind+1)->value.end()) << "\":[" <<print_vector(res.value) << "],";
 				attr_values += temp_attr_val.str();
 #ifdef _DEBUG_FORMULA
 				DEBUG_STREAM << "		node event.normal -> " << print_vector(res.value) << endl;
@@ -4759,13 +4771,13 @@ formula_res_t AlarmHandler::eval_expression(iter_t const& i, string &attr_values
 				return res;
 			}
 		}
-		else
+		else if(child_ind+1 < i->children.size() -1)//unsupported more indexes/qualities
 		{
-        	err <<  "in node event_ID(" << string(i->value.begin(), i->value.end()) << ") children2 is not an index ->" << string((i->children.begin()+1)->value.begin(), (i->children.begin()+1)->value.end());
+        	err <<  "expecting indexes or properties after attribute name, found instead: "<< string((i->children.begin()+child_ind+1)->value.begin(), (i->children.begin()+child_ind+1)->value.end());
         	throw err.str(); 
         }
 		if(!ind.value.empty())
-			return eval_expression(i->children.begin(), attr_values, (int)ind.value[0]);
+			return eval_expression(i->children.begin(), attr_values, ind.value);
 		else
 			return eval_expression(i->children.begin(), attr_values);
     }    
@@ -4792,6 +4804,8 @@ formula_res_t AlarmHandler::eval_expression(iter_t const& i, string &attr_values
 					err <<  "attribute " << string(i->value.begin(), i->value.end()) << " value not valid while evaluating formula";
 				formula_res_t res;
 				res.valid = false;
+				res.dim_x = it->dim_x;
+				res.dim_y = it->dim_y;
 				res.quality = it->quality;
 				res.ex_reason = it->ex_reason;
 				res.ex_desc = it->ex_desc;
@@ -4816,6 +4830,8 @@ formula_res_t AlarmHandler::eval_expression(iter_t const& i, string &attr_values
 					err <<  "attribute " << string(i->value.begin(), i->value.end()) << " value not initialized while evaluating formula";
 				formula_res_t res;
 				res.valid = false;
+				res.dim_x = it->dim_x;
+				res.dim_y = it->dim_y;
 				res.quality = it->quality;
 				res.ex_reason = it->ex_reason;
 				res.ex_desc = it->ex_desc;
@@ -4831,33 +4847,113 @@ formula_res_t AlarmHandler::eval_expression(iter_t const& i, string &attr_values
 				events->veclock.readerOut();
         		//throw err.str();
 				return res;
-        	}        	
+        	} 
+			formula_res_t res;
+			res.valid = true;
+			res.dim_x = it->dim_x;
+			res.dim_y = it->dim_y;
+			res.quality = it->quality;
+			res.ex_reason = it->ex_reason;
+			res.ex_desc = it->ex_desc;
+			res.ex_origin = it->ex_origin;
+			res.attr_name = it->name;    	
 			ostringstream temp_attr_val;
 			if(it->value.size() > 0)
 			{
-				if(ev_ind > 0)
-					temp_attr_val << "\"" <<  it->name << "[" << ev_ind << "]\":[" <<it->value.at(ev_ind) << "],";//throw  std::out_of_range
+				if(!ev_ind.empty())
+				{
+					size_t ev_ind_1d = ev_ind[0];
+					temp_attr_val << "\"" <<  it->name;
+					for(auto ei : ev_ind)
+					{
+						temp_attr_val << "[" << ei << "]";
+					}
+					temp_attr_val << "\":";
+					if(ev_ind.size() ==1 && it->dim_y <=1)//single element of 1D array
+					{
+						res.dim_x = 1;
+						res.dim_y = 0;
+						if(ev_ind[0] >= it->value.size())
+						{
+							err << "Requested element " << ev_ind[0] << " is out of bounds with dim=" << it->value.size();
+							events->veclock.readerOut();
+							DEBUG_STREAM << __func__ << ": " << err.str() << endl;
+							Tango::Except::throw_exception(	//throw exception to have error not delayed otherwise it is subject to err_delay (delayed error notification)
+								(const char *)"OUT_OF_RANGE",
+								err.str(),
+								it->name);
+						}
+						res.value = {it->value.at(ev_ind[0])};//throw  std::out_of_range
+						temp_attr_val << "[" << res.value[0] << "],";
+					}
+					else if(ev_ind.size() == 1 && it->dim_y > 1)//single row of 2D array
+					{
+						res.dim_y = 0;
+						if(ev_ind[0] >= it->dim_y)
+						{
+							err << "Requested row " << ev_ind[0] << " is out of bounds with dim_y=" << it->dim_y;
+							events->veclock.readerOut();
+							DEBUG_STREAM << __func__ << ": " << err.str() << endl;
+							Tango::Except::throw_exception(	//throw exception to have error not delayed otherwise it is subject to err_delay (delayed error notification)
+								(const char *)"OUT_OF_RANGE",
+								err.str(),
+								it->name);
+						}
+						auto vstart = it->value.begin();
+						auto vend = it->value.begin();
+						advance(vstart,(int)(ev_ind[0] * (it->dim_x)));
+						advance(vend,(int)(ev_ind[0]+1)*it->dim_x);
+						res.value = value_t(vstart,vend);
+						temp_attr_val << "[" << print_vector(res.value) << "],";
+					}
+					else if(ev_ind.size() == 2 && it->dim_y > 1)//single element of 2D array
+					{
+						res.dim_x = 1;
+						res.dim_y = 0;
+						if(ev_ind[0] >= it->dim_y)
+						{
+							err << "Requested row " << ev_ind[0] << " is out of bounds with dim_y=" << it->dim_y;
+							events->veclock.readerOut();
+							DEBUG_STREAM << __func__ << ": " << err.str() << endl;
+							Tango::Except::throw_exception(	//throw exception to have error not delayed otherwise it is subject to err_delay (delayed error notification)
+								(const char *)"OUT_OF_RANGE",
+								err.str(),
+								it->name);
+						}
+						if(ev_ind[1] >= it->dim_x)
+						{
+							err << "Requested column " << ev_ind[1] << " is out of bounds with dim_x=" << it->dim_x;
+							events->veclock.readerOut();
+							DEBUG_STREAM << __func__ << ": " << err.str() << endl;
+							Tango::Except::throw_exception(	//throw exception to have error not delayed otherwise it is subject to err_delay (delayed error notification)
+								(const char *)"OUT_OF_RANGE",
+								err.str(),
+								it->name);
+						}
+						res.value = {it->value.at(ev_ind[0]*it->dim_x + ev_ind[1])};//throw  std::out_of_range
+						temp_attr_val << "[" <<res.value[0] << "],";//throw  std::out_of_range
+					}
+					else //TODO N-DIMENSIONAL arrays
+					{
+						err << "UNSUPPORTED array indexes: " << ev_ind.size() << " indexes given, dim_x=" << it->dim_x << " dim_y=" << it->dim_y;
+						DEBUG_STREAM << __func__ << ": " << err.str() << endl;
+						events->veclock.readerOut();
+						throw err.str();
+					}
+				}
 				else
-					temp_attr_val << "\"" <<  it->name << "\":[" <<print_vector(it->value) << "],";
+				{
+					res.value = it->value;
+					temp_attr_val << "\"" <<  it->name << "\":[" <<print_vector(res.value) << "],";
+				}
 			}
 			else
 				temp_attr_val << "\"" <<  it->name << "\":[],";
 			attr_values += temp_attr_val.str();
-			formula_res_t res;
-			res.valid = true;
-			res.quality = it->quality;
-			res.ex_reason = it->ex_reason;
-			res.ex_desc = it->ex_desc;
-			res.ex_origin = it->ex_origin;
-			res.attr_name = it->name;
+			events->veclock.readerOut();
 #ifdef _DEBUG_FORMULA
 			DEBUG_STREAM << "		node name -> " << temp_attr_val.str() << " quality=" << res.quality << endl;
-#endif
-			if(ev_ind >=0)
-				res.value = {(it->value.at(ev_ind))};		//throw  std::out_of_range
-			else
-				res.value = it->value;
-			events->veclock.readerOut();
+#endif  
 			return 	res;
 		}
 		else
@@ -4958,7 +5054,7 @@ formula_res_t AlarmHandler::eval_expression(iter_t const& i, string &attr_values
 				res.ex_desc = res.combine_exception(res_1.ex_desc, res_2.ex_desc);
 				res.ex_origin = res.combine_exception(res_1.ex_origin, res_2.ex_origin);
 			}
-			vectorUtils::applyVectorFunc(val_l1, val_l2,res.value, "Bitwise &", vectorUtils::fBitAnd);
+			vectorUtils::applyVectorFunc(val_l1, res_1.dim_x,res_1.dim_y,val_l2,res_2.dim_x,res_2.dim_y,res.value,res.dim_x,res.dim_y, "Bitwise &", vectorUtils::fBitAnd);
             return res;
         }
         else if (*i->value.begin() == '|')
@@ -4972,7 +5068,7 @@ formula_res_t AlarmHandler::eval_expression(iter_t const& i, string &attr_values
 				res.ex_desc = res.combine_exception(res_1.ex_desc, res_2.ex_desc);
 				res.ex_origin = res.combine_exception(res_1.ex_origin, res_2.ex_origin);
 			}
-			vectorUtils::applyVectorFunc(val_l1, val_l2,res.value, "Bitwise |", vectorUtils::fBitOr);
+			vectorUtils::applyVectorFunc(val_l1, res_1.dim_x,res_1.dim_y, val_l2,res_2.dim_x,res_2.dim_y, res.value,res.dim_x,res.dim_y, "Bitwise |", vectorUtils::fBitOr);
             return res;
         }  
         else if (*i->value.begin() == '^')
@@ -4986,7 +5082,7 @@ formula_res_t AlarmHandler::eval_expression(iter_t const& i, string &attr_values
 				res.ex_desc = res.combine_exception(res_1.ex_desc, res_2.ex_desc);
 				res.ex_origin = res.combine_exception(res_1.ex_origin, res_2.ex_origin);
 			}
-			vectorUtils::applyVectorFunc(val_l1, val_l2,res.value, "Bitwise ^", vectorUtils::fBitXor);
+			vectorUtils::applyVectorFunc(val_l1, res_1.dim_x,res_1.dim_y, val_l2,res_2.dim_x,res_2.dim_y, res.value,res.dim_x,res.dim_y, "Bitwise ^", vectorUtils::fBitXor);
             return res;
         }  
         else
@@ -5045,7 +5141,7 @@ formula_res_t AlarmHandler::eval_expression(iter_t const& i, string &attr_values
 				res.ex_desc = res.combine_exception(res_1.ex_desc, res_2.ex_desc);
 				res.ex_origin = res.combine_exception(res_1.ex_origin, res_2.ex_origin);
 			}
-			vectorUtils::applyVectorFunc(val_l1, val_l2,res.value, "Bitwise <<", vectorUtils::fBitShiftL);
+			vectorUtils::applyVectorFunc(val_l1, res_2.dim_x,res_2.dim_y, val_l2,res_2.dim_x,res_2.dim_y, res.value,res.dim_x,res.dim_y, "Bitwise <<", vectorUtils::fBitShiftL);
             return res;
         }
         else if (string(i->value.begin(), i->value.end()) == string(">>"))
@@ -5059,7 +5155,7 @@ formula_res_t AlarmHandler::eval_expression(iter_t const& i, string &attr_values
 				res.ex_desc = res.combine_exception(res_1.ex_desc, res_2.ex_desc);
 				res.ex_origin = res.combine_exception(res_1.ex_origin, res_2.ex_origin);
 			}
-			vectorUtils::applyVectorFunc(val_l1, val_l2,res.value, "Bitwise >>", vectorUtils::fBitShiftR);
+			vectorUtils::applyVectorFunc(val_l1, res_1.dim_x,res_1.dim_y, val_l2,res_2.dim_x,res_2.dim_y, res.value,res.dim_x,res.dim_y, "Bitwise >>", vectorUtils::fBitShiftR);
             return res;
         }  
         else
@@ -5106,6 +5202,8 @@ formula_res_t AlarmHandler::eval_expression(iter_t const& i, string &attr_values
 						if(it->ex_desc.length() > 0)
 							err << " EX: '" << it->ex_desc << "'";
 						res.valid = false;
+						res.dim_x = it->dim_x;
+						res.dim_y = it->dim_y;
 						res.quality = it->quality;
 						res.ex_reason = it->ex_reason;
 						res.ex_desc = it->ex_desc;
@@ -5123,6 +5221,8 @@ formula_res_t AlarmHandler::eval_expression(iter_t const& i, string &attr_values
 						if(it->ex_desc.length() > 0)
 							err << " EX: '" << it->ex_desc << "'";
 						res.valid = false;
+						res.dim_x = it->dim_x;
+						res.dim_y = it->dim_y;
 						res.quality = it->quality;
 						res.ex_reason = it->ex_reason;
 						res.ex_desc = it->ex_desc;
@@ -5138,6 +5238,8 @@ formula_res_t AlarmHandler::eval_expression(iter_t const& i, string &attr_values
 					temp_attr_val << "\"" << it->name << "\":\"" <<it->value_string << "\",";
 					attr_values += temp_attr_val.str();
 					res.valid = true;
+					res.dim_x = it->dim_x;
+					res.dim_y = it->dim_y;
 					res.quality = it->quality;
 					res.ex_reason = it->ex_reason;
 					res.ex_desc = it->ex_desc;
@@ -5307,7 +5409,7 @@ formula_res_t AlarmHandler::eval_expression(iter_t const& i, string &attr_values
 		if (string(i->value.begin(), i->value.end()) == string("min"))
 		{
         	formula_res_t res;
-			vectorUtils::applyVectorFunc(res_1.value, res_2.value, res.value, string(i->value.begin(), i->value.end()), vectorUtils::fMin);
+			vectorUtils::applyVectorFunc(res_1.value, res_1.dim_x,res_1.dim_y, res_2.value, res_2.dim_x,res_2.dim_y, res.value,res.dim_x,res.dim_y, string(i->value.begin(), i->value.end()), vectorUtils::fMin);
 			res.valid = valid;
         	res.quality = res.combine_quality(res_1.quality, res_2.quality);
 			if(!res.valid)
@@ -5321,7 +5423,7 @@ formula_res_t AlarmHandler::eval_expression(iter_t const& i, string &attr_values
 		else if (string(i->value.begin(), i->value.end()) == string("max"))
 		{
         	formula_res_t res;
-			vectorUtils::applyVectorFunc(res_1.value, res_2.value, res.value, string(i->value.begin(), i->value.end()), vectorUtils::fMax);
+			vectorUtils::applyVectorFunc(res_1.value, res_1.dim_x,res_1.dim_y, res_2.value, res_2.dim_x,res_2.dim_y, res.value,res.dim_x,res.dim_y, string(i->value.begin(), i->value.end()), vectorUtils::fMax);
 			res.valid = valid;
         	res.quality = res.combine_quality(res_1.quality, res_2.quality);
 			if(!res.valid)
@@ -5335,7 +5437,7 @@ formula_res_t AlarmHandler::eval_expression(iter_t const& i, string &attr_values
 		else if (string(i->value.begin(), i->value.end()) == string("pow"))
 		{
         	formula_res_t res;
-			vectorUtils::applyVectorFunc(res_1.value, res_2.value, res.value, string(i->value.begin(), i->value.end()), vectorUtils::fPow);
+			vectorUtils::applyVectorFunc(res_1.value, res_1.dim_x,res_1.dim_y, res_2.value, res_2.dim_x,res_2.dim_y, res.value,res.dim_x,res.dim_y, string(i->value.begin(), i->value.end()), vectorUtils::fPow);
 			res.valid = valid;
         	res.quality = res.combine_quality(res_1.quality, res_2.quality);
 			if(!res.valid)
diff --git a/src/AlarmHandler.h b/src/AlarmHandler.h
index de47f24..d8b6007 100644
--- a/src/AlarmHandler.h
+++ b/src/AlarmHandler.h
@@ -607,7 +607,7 @@ private:
 	formula_res_t eval_formula(tree_parse_info_t tree, string &attr_values);
 	void find_event_formula(tree_parse_info_t tree, vector<string> &);
 	
-	formula_res_t eval_expression(iter_t const& i, string &attr_values, int ev_ind=-1);			//recursive tree node evaluation
+	formula_res_t eval_expression(iter_t const& i, string &attr_values, vector<double> ev_ind={});			//recursive tree node evaluation
 	void eval_node_event(iter_t const& i, vector<string> & ev);		//recursive tree node evaluation		
 
 	void prepare_alarm_attr();	//for read attribute alarm and push_change_event
diff --git a/src/alarm_table.h b/src/alarm_table.h
index 5d396bd..d3701ec 100644
--- a/src/alarm_table.h
+++ b/src/alarm_table.h
@@ -149,16 +149,29 @@ static double fAnd(double a, double b){	return (double)((bool)a && (bool)b);}
 static double fOr(double a, double b){	return (double)((bool)a || (bool)b);}
 
 template<class T >
-static void applyVectorFunc(const vector<T> & v1, const vector<T> & v2, value_t &res, const string& origin, double (*func)(T, T))
+static void applyVectorFunc(const vector<T> & v1, long v1_x, long v1_y, const vector<T> & v2, long v2_x, long v2_y, value_t &res, long &res_x, long &res_y, const string& origin, double (*func)(T, T))
 {
 	if(v1.size() > 0 && v2.size() > 0 && (v1.size() == v2.size()))
 	{
+		res_x = v1_x;
+		res_y = v1_y;
+		if(v1_x != v2_x || (v1_y != v2_y && (v1_y > 1 || v2_y > 1)))//dim_y 0 and 1 are the same
+		{
+			ostringstream o;
+			o << "Array sizes do not match: " << (v1_y ? v1_y : 1) << "x" << v1_x <<  " != " << (v2_y ? v2_y : 1) << "x" << v2_x;
+			Tango::Except::throw_exception( //throw exception to have error not delayed
+				"FORMULA_ERROR",
+				o.str(),
+				origin);
+		}
 		res.resize(v1.size());
 		std::transform(v1.begin(), v1.end(), v2.begin(),res.begin(),
 					[func](T a, T b) { return func(a, b); });				
 	}
 	else if(v2.size() == 1)
 	{
+		res_x = v1_x;
+		res_y = v1_y;
 		T b = v2[0];
 		res.resize(v1.size());
 		std::transform(v1.begin(), v1.end(), res.begin(),
@@ -166,6 +179,8 @@ static void applyVectorFunc(const vector<T> & v1, const vector<T> & v2, value_t
 	}
 	else if(v1.size() == 1)
 	{
+		res_x = v2_x;
+		res_y = v2_y;
 		T a = v1[0];
 		res.resize(v2.size());
 		std::transform(v2.begin(), v2.end(), res.begin(),
@@ -174,7 +189,7 @@ static void applyVectorFunc(const vector<T> & v1, const vector<T> & v2, value_t
 	else if(v1.size() > 0 && v2.size() > 0)
 	{
 		ostringstream o;
-		o << "Array sizes do not match: " << v1.size() << "!=" <<v2.size();
+		o << "Array sizes do not match: " << (v1_y ? v1_y : 1) << "x" << v1_x <<  " != " << (v2_y ? v2_y : 1) << "x" << v2_x;
 		Tango::Except::throw_exception( //throw exception to have error not delayed
 			"FORMULA_ERROR",
 			o.str(),
@@ -185,8 +200,10 @@ static void applyVectorFunc(const vector<T> & v1, const vector<T> & v2, value_t
 
 struct formula_res_t
 {
-	formula_res_t(){valid=false;quality=Tango::ATTR_VALID;ex_reason=string("");ex_desc=string("");ex_origin=string("");}
+	formula_res_t(){valid=false;quality=Tango::ATTR_VALID;ex_reason=string("");ex_desc=string("");ex_origin=string("");dim_x=0;dim_y=0;}
 	value_t value;
+	long dim_x;
+	long dim_y;
 	int quality;
 	bool valid;
 	string error;
@@ -233,7 +250,7 @@ struct formula_res_t
 				res.ex_desc = combine_exception(ex_desc, e.ex_desc);
 				res.ex_origin = combine_exception(ex_origin, e.ex_origin);
 			}
-			vectorUtils::applyVectorFunc(value,e.value,res.value,__func__,vectorUtils::fEqual);
+			vectorUtils::applyVectorFunc(value,dim_x,dim_y,e.value,e.dim_x,e.dim_y,res.value,res.dim_x,res.dim_y,__func__,vectorUtils::fEqual);
 			return res;
 		}
 	formula_res_t operator!=(const formula_res_t& e)
@@ -247,7 +264,7 @@ struct formula_res_t
 				res.ex_desc = combine_exception(ex_desc, e.ex_desc);
 				res.ex_origin = combine_exception(ex_origin, e.ex_origin);
 			}
-			vectorUtils::applyVectorFunc(value,e.value,res.value,__func__,vectorUtils::fDifferent);
+			vectorUtils::applyVectorFunc(value,dim_x,dim_y,e.value,e.dim_x,e.dim_y,res.value,res.dim_x,res.dim_y,__func__,vectorUtils::fDifferent);
 			return res;
 		}
 	formula_res_t operator<=(const formula_res_t& e)
@@ -261,7 +278,7 @@ struct formula_res_t
 				res.ex_desc = combine_exception(ex_desc, e.ex_desc);
 				res.ex_origin = combine_exception(ex_origin, e.ex_origin);
 			}
-			vectorUtils::applyVectorFunc(value,e.value,res.value,__func__,vectorUtils::fLessEqual);
+			vectorUtils::applyVectorFunc(value,dim_x,dim_y,e.value,e.dim_x,e.dim_y,res.value,res.dim_x,res.dim_y,__func__,vectorUtils::fLessEqual);
 			return res;
 		}
 	formula_res_t operator>=(const formula_res_t& e)
@@ -275,7 +292,7 @@ struct formula_res_t
 				res.ex_desc = combine_exception(ex_desc, e.ex_desc);
 				res.ex_origin = combine_exception(ex_origin, e.ex_origin);
 			}
-			vectorUtils::applyVectorFunc(value,e.value,res.value,__func__,vectorUtils::fGreaterEqual);
+			vectorUtils::applyVectorFunc(value,dim_x,dim_y,e.value,e.dim_x,e.dim_y,res.value,res.dim_x,res.dim_y,__func__,vectorUtils::fGreaterEqual);
 			return res;
 		}
 	formula_res_t operator<(const formula_res_t& e)
@@ -289,7 +306,7 @@ struct formula_res_t
 				res.ex_desc = combine_exception(ex_desc, e.ex_desc);
 				res.ex_origin = combine_exception(ex_origin, e.ex_origin);
 			}
-			vectorUtils::applyVectorFunc(value,e.value,res.value,__func__,vectorUtils::fLess);
+			vectorUtils::applyVectorFunc(value,dim_x,dim_y,e.value,e.dim_x,e.dim_y,res.value,res.dim_x,res.dim_y,__func__,vectorUtils::fLess);
 			return res;
 		}
 	formula_res_t operator>(const formula_res_t& e)
@@ -303,7 +320,7 @@ struct formula_res_t
 				res.ex_desc = combine_exception(ex_desc, e.ex_desc);
 				res.ex_origin = combine_exception(ex_origin, e.ex_origin);
 			}
-			vectorUtils::applyVectorFunc(value,e.value,res.value,__func__,vectorUtils::fGreater);
+			vectorUtils::applyVectorFunc(value,dim_x,dim_y,e.value,e.dim_x,e.dim_y,res.value,res.dim_x,res.dim_y,__func__,vectorUtils::fGreater);
 			return res;
 		}
 	formula_res_t operator||(const formula_res_t& e)
@@ -325,7 +342,7 @@ struct formula_res_t
 			}
 			if(!(value.size() <= 1 && e.value.size() <= 1))
 			{
-				vectorUtils::applyVectorFunc(value,e.value,res.value,__func__,vectorUtils::fOr);
+				vectorUtils::applyVectorFunc(value,dim_x,dim_y,e.value,e.dim_x,e.dim_y,res.value,res.dim_x,res.dim_y,__func__,vectorUtils::fOr);
 			}
 			return res;
 		}
@@ -348,7 +365,7 @@ struct formula_res_t
 			}
 			if(!(value.size() <= 1 && e.value.size() <= 1))
 			{
-				vectorUtils::applyVectorFunc(value,e.value,res.value,__func__,vectorUtils::fAnd);
+				vectorUtils::applyVectorFunc(value,dim_x,dim_y,e.value,e.dim_x,e.dim_y,res.value,res.dim_x,res.dim_y,__func__,vectorUtils::fAnd);
 			}
 			return res;
 		}
@@ -363,7 +380,7 @@ struct formula_res_t
 				res.ex_desc = combine_exception(ex_desc, e.ex_desc);
 				res.ex_origin = combine_exception(ex_origin, e.ex_origin);
 			}
-			vectorUtils::applyVectorFunc(value,e.value,res.value,__func__,vectorUtils::fPlus);
+			vectorUtils::applyVectorFunc(value,dim_x,dim_y,e.value,e.dim_x,e.dim_y,res.value,res.dim_x,res.dim_y,__func__,vectorUtils::fPlus);
 			return res;
 		}
 	formula_res_t operator-(const formula_res_t& e)
@@ -377,7 +394,7 @@ struct formula_res_t
 				res.ex_desc = combine_exception(ex_desc, e.ex_desc);
 				res.ex_origin = combine_exception(ex_origin, e.ex_origin);
 			}
-			vectorUtils::applyVectorFunc(value,e.value,res.value,__func__,vectorUtils::fMinus);
+			vectorUtils::applyVectorFunc(value,dim_x,dim_y,e.value,e.dim_x,e.dim_y,res.value,res.dim_x,res.dim_y,__func__,vectorUtils::fMinus);
 			return res;
 		}
 	formula_res_t operator*(const formula_res_t& e)
@@ -391,7 +408,7 @@ struct formula_res_t
 				res.ex_desc = combine_exception(ex_desc, e.ex_desc);
 				res.ex_origin = combine_exception(ex_origin, e.ex_origin);
 			}
-			vectorUtils::applyVectorFunc(value,e.value,res.value,__func__,vectorUtils::fMult);
+			vectorUtils::applyVectorFunc(value,dim_x,dim_y,e.value,e.dim_x,e.dim_y,res.value,res.dim_x,res.dim_y,__func__,vectorUtils::fMult);
 			return res;
 		}
 	formula_res_t operator/(const formula_res_t& e)
@@ -405,7 +422,7 @@ struct formula_res_t
 				res.ex_desc = combine_exception(ex_desc, e.ex_desc);
 				res.ex_origin = combine_exception(ex_origin, e.ex_origin);
 			}
-			vectorUtils::applyVectorFunc(value,e.value,res.value,__func__,vectorUtils::fDiv);
+			vectorUtils::applyVectorFunc(value,dim_x,dim_y,e.value,e.dim_x,e.dim_y,res.value,res.dim_x,res.dim_y,__func__,vectorUtils::fDiv);
 			return res;
 		}
 	formula_res_t operator!()
diff --git a/src/event_table.cpp b/src/event_table.cpp
index f52d904..2f9f977 100644
--- a/src/event_table.cpp
+++ b/src/event_table.cpp
@@ -1278,7 +1278,7 @@ void EventCallBack::push_event(Tango::EventData* ev)
 			e.ev_name = ev->attr_name;
 #endif
 			e.ts = ev->attr_value->time;
-			extract_values(ev->attr_value, e.value, e.value_string, e.type, e.read_size);
+			extract_values(ev->attr_value, e.value, e.value_string, e.type, e.read_size, e.dim_x, e.dim_y);
 		} else {
 			e.quality = Tango::ATTR_INVALID;
 #if 0//TANGO_VER >= 711
@@ -1341,7 +1341,7 @@ void EventCallBack::push_event(Tango::EventData* ev)
 	static_cast<AlarmHandler_ns::AlarmHandler *>(mydev)->evlist.push_back(e);
 }  /* push_event() */
 
-void EventCallBack::extract_values(Tango::DeviceAttribute *attr_value, vector<double> &val, string &val_string, int &type, int &read_size)
+void EventCallBack::extract_values(Tango::DeviceAttribute *attr_value, vector<double> &val, string &val_string, int &type, int &read_size, long &dim_x, long &dim_y)
 {
 	Tango::DevState stval;
 	vector<Tango::DevState> v_st;
@@ -1375,6 +1375,8 @@ void EventCallBack::extract_values(Tango::DeviceAttribute *attr_value, vector<do
                 attr_r_dim.dim_y = 0;
                 //attr_w_dim.dim_y = 0;
         }
+		dim_x = attr_r_dim.dim_x;
+		dim_y = attr_r_dim.dim_y;
         read_size = attr_r_dim.dim_x;
         if(attr_r_dim.dim_y > 1)
                 read_size *= attr_r_dim.dim_y;
diff --git a/src/event_table.h b/src/event_table.h
index 764369b..21758e0 100644
--- a/src/event_table.h
+++ b/src/event_table.h
@@ -66,7 +66,8 @@ class event {
 		int type,							/* attribute data type */
 				read_size,                                      /* attribute size of read part */
 				counter,					/* molteplicita' */
-				err_counter;					/* molteplicita' errore */				
+				err_counter;					/* molteplicita' errore */		
+		long dim_x, dim_y;		
 		alarm_list m_alarm;
 		bool valid;	//TODO: old
 		bool 	first;//TODO: new
@@ -116,6 +117,8 @@ typedef struct basic_event_info_s {
 	string ex_origin;
 	int type;
 	int read_size;
+	long dim_x;
+	long dim_y;
 	Tango::TimeVal ts;
 	string msg;
 } bei_t;
@@ -202,7 +205,7 @@ class EventCallBack : public Tango::CallBack, public Tango::LogAdapter
 		~EventCallBack(void);
 		void push_event(Tango::EventData* ev);
 		//void init(event_list* e);
-		void extract_values(Tango::DeviceAttribute *attr_value, vector<double> &val, string &val_string, int &type, int &read_size);
+		void extract_values(Tango::DeviceAttribute *attr_value, vector<double> &val, string &val_string, int &type, int &read_size, long &dim_x, long &dim_y);
 	private:
 		//event_list* e_ptr;
 		Tango::DeviceImpl *mydev;
diff --git a/src/formula_grammar.h b/src/formula_grammar.h
index 0aba5d0..8a83118 100644
--- a/src/formula_grammar.h
+++ b/src/formula_grammar.h
@@ -263,8 +263,8 @@ struct formula_grammar : public grammar<formula_grammar>
 
 			event_
 				=	name
-					>> !( (index)
-						| (property)
+					>> ( *(index)//0 or more indexex
+						>> !(property) //followed by 0 or 1 property
 						)
 				;				
 
-- 
GitLab