From bf08b19dc9c45220a2761805f832798dde1564a8 Mon Sep 17 00:00:00 2001
From: "ezekiel.dohmen" <ezekiel.dohmen@ligo.org>
Date: Mon, 22 Jul 2024 15:20:57 -0700
Subject: [PATCH] Fixing a bug with multiple DACs in your model having the same
 card_num, fixing ADC top level check

---
 src/epics/util/feCodeGen.pl      |  6 ++++++
 src/epics/util/lib/Adc.pm        |  2 +-
 src/epics/util/lib/Dac_common.pm |  7 +++++++
 src/epics/util/lib/Parser3.pm    | 36 +++++++++++++++++++-------------
 4 files changed, 35 insertions(+), 16 deletions(-)

diff --git a/src/epics/util/feCodeGen.pl b/src/epics/util/feCodeGen.pl
index 2a3a89721..ebadc9222 100755
--- a/src/epics/util/feCodeGen.pl
+++ b/src/epics/util/feCodeGen.pl
@@ -1748,6 +1748,8 @@ for(@all_epics_vars) {
 	print OUTH "\t\U$systemName \L$systemName;\n";
 	print OUTH "} CDS_EPICS;\n";
 	print OUTH "\#define TARGET_HOST_NAME $targetHost\n";
+
+    #TODO: These don't appear to be used, should we remove?
 	if ($requireIOcnt) {
 		print OUTH "\#define TARGET_ADC_COUNT $adcCnt\n";
 		for (0 .. $dacCnt-1) {
@@ -1766,6 +1768,7 @@ for(@all_epics_vars) {
 		print OUTH "\#define TARGET_DAC20_COUNT $dac20Cnt\n";
 		print OUTH "\#define TARGET_LIGO_DAC_COUNT $dacligoCnt\n";
 	} else {
+
 		if($virtualiop == 0 and $iopModel == 1) {
 			print OUTH "\#define TARGET_ADC_COUNT 1\n";
 		} else {
@@ -1776,6 +1779,9 @@ for(@all_epics_vars) {
 		print OUTH "\#define TARGET_DAC20_COUNT 0\n";
 		print OUTH "\#define TARGET_LIGO_DAC_COUNT 0\n";
 	}
+
+
+
     if ($edcu) {
         $no_daq = 1;
 	    print OUTH "\#define TARGET_RT_FLAG 0\n";
diff --git a/src/epics/util/lib/Adc.pm b/src/epics/util/lib/Adc.pm
index adf920ab9..51c2ae626 100644
--- a/src/epics/util/lib/Adc.pm
+++ b/src/epics/util/lib/Adc.pm
@@ -155,7 +155,7 @@ sub frontEndInitCode {
     foreach (0 .. $::partCnt) {
         foreach  $inp (0 .. $::partInCnt[$_]) {
             if ("Adc" eq $::partInputType[$_][$inp] && $anum == $::partInNum[$_][$inp]) {
-                #print $_," ", $::xpartName[$_], " ", $::partInputPort[$_][$inp], "\n";
+                print $_," ", $::xpartName[$_], " ", $::partInputPort[$_][$inp], "\n";
                 $seen{$::partInputPort[$_][$inp]}=1;
             }
         }
diff --git a/src/epics/util/lib/Dac_common.pm b/src/epics/util/lib/Dac_common.pm
index 8b3ced937..a98977d91 100644
--- a/src/epics/util/lib/Dac_common.pm
+++ b/src/epics/util/lib/Dac_common.pm
@@ -12,6 +12,13 @@ use strict;
 );
 $Dac_common::default_board_type = "GSC_16AO16";
 
+%Dac_common::supported_board_types = (
+    "Dac" => 1,
+    "Dac18" => 1,
+    "Dac20" => 1,
+    "Dacligo28" => 1
+);
+
 #This constant needs to be matched to the MAX_DAC_CHN_PER_MOD constant
 #in cdsHardware.h, so the c module allocates buffers appropriately
 $Dac_common::max_dac_channels = 32;
\ No newline at end of file
diff --git a/src/epics/util/lib/Parser3.pm b/src/epics/util/lib/Parser3.pm
index 122346af7..08eedf928 100644
--- a/src/epics/util/lib/Parser3.pm
+++ b/src/epics/util/lib/Parser3.pm
@@ -10,7 +10,7 @@ use Cwd;
 #//
 #// \n
 
-
+require "lib/Dac_common.pm";
 require "lib/Tree.pm";
 
 # Hash of subsystem-annotated part names into part numbers
@@ -1823,13 +1823,18 @@ sub process {
   my @dac_card_nums;
 
   foreach (0 ... $::partCnt) {
-  	#if ($::partType[$_] eq "Dac" || $::partType[$_] eq "Dac18" || $::partType[$_] eq "Dac20") {
-  	if ($::partType[$_] eq "Dac" || $::partType[$_] eq "Dac18" || $::partType[$_] eq "Dacligo28") {
+
+  	if (exists $Dac_common::supported_board_types{$::partType[$_]}) {
 		my $card_num = $::dacNum[$::card2array[$_]];
-		print "Dac ", $::xpartName[$_], " ", $card_num, " ", $::partType[$_], "\n";
-		push @dac_card_nums, $card_num;
+		#print "Dac ", $::xpartName[$_], " ", $card_num, " ", $::partType[$_], "\n";
+		push @dac_card_nums, $::partType[$_] ."_". $card_num;
 
 	} elsif ($::partType[$_] eq "Adc") {
+
+        if ($::partSubName[$_] ne "") {
+            die "ERROR: All ADCs must be on the top level in the model. Failed: $::xpartName[$_]";
+        }
+
 		my $an = substr($::xpartName[$_],3,2);
 		my $card_num = $::adcNum[$an];
 		#print "Adc ", $::xpartName[$_], " ", $card_num, "\n";
@@ -1841,7 +1846,7 @@ sub process {
   # Check that card numbers are unique
   my %hash   = map { $_, 1 } @dac_card_nums;
   my @unique = keys %hash;
-  die "ERROR: DAC card numbers must be unique\n" unless $#dac_card_nums == $#unique;
+  die "ERROR: card_num= must be unique across DACs of the same type.\n" unless $#dac_card_nums == $#unique;
 
   my %hash   = map { $_, 1 } @adc_card_nums;
   my @unique = keys %hash;
@@ -1852,22 +1857,23 @@ sub process {
   foreach (0 .. $#adc_names) {
 	#printf "ADC #%s\n", $adc_names[$_];
 	if ($_ && $adc_names[$_-1] + 1 != $adc_names[$_]) {
-		die "ERROR: ADC names must be unique and consecutive\n";
+		die "ERROR: ADC names must be unique and consecutive. Failed on $adc_names[$_]\n";
 	}
 
     if ($_ ne $adc_names[$_]) {
-        die "ERROR: ADC blocks must start at 0 and be  consecutive\n";
+        die "ERROR: ADC blocks must start at 0 and be consecutive\n";
     }
 
   }
 
   # See to it that ADC is on the top level, this makes all IO more visable
     foreach (0 ... $::partCnt) {
-        if ($::partType[$_] eq "Dac" || $::partType[$_] eq "Dac18" || $::partType[$_] eq "Dac20") {  
-            if ($::partSubName[$_] ne "") {
-                die "ERROR: All ADCs and DACs must be on the top level in the model";
-            }
+
+        #If the partType is of DAC type and it's not at the TOP level.
+        if( exists $Dac_common::supported_board_types{$::partType[$_]} && $::partSubName[$_] ne "") {
+            die "ERROR: All DACs must be on the top level in the model.";
         }
+
         if ($::partType[$_] =~ /^IPCx/ || $::partType[$_] eq "Contec1616DIO" || $::partType[$_] eq "Contec6464DIO" || $::partType[$_] eq "CDO32" 
             || $::partType[$_] eq "CDO64" || $::partType[$_] eq "CDI64" || $::partType[$_] eq "Dio" || $::partType[$_] eq "Rio" 
             || $::partType[$_] eq "Rio1" ) {
@@ -1879,8 +1885,8 @@ sub process {
         if ( $::partType[$_] ne "BUSS" && 
 	     $::partType[$_] ne "Parameters" && 
 	     $::partType[$_] ne "" &&
-             $::partType[$_] ne "INPUT" &&
-             $::partType[$_] ne "OUTPUT" &&
+         $::partType[$_] ne "INPUT" &&
+         $::partType[$_] ne "OUTPUT" &&
 	     $::partType[$_] ne "BUSC" &&
 	     $::partType[$_] ne "TERM" &&
 	     $::partType[$_] ne "FunctionCall" 
@@ -1898,7 +1904,7 @@ sub process {
             print "filteredPartName: $filteredPartName\n";
 
             if ( not $filteredPartName =~ $regexForPartType ) {
-		print "partType: $::partType[$_]\n";
+		        print "partType: $::partType[$_]\n";
                 die "ERROR: The block name: $filteredPartName , full name: $::xpartName[$_] , is not a valid c identifier. "
                     . "Please start block names with a letter, then only use letters, numbers and underscores for the rest of the name.";
             }
-- 
GitLab