ArrowVHDL-1.0: src/System/ArrowVHDL/Circuit/Workers.hs
-- In diesem Modul werden eine Reihe von \begriff{worker}-Funktionen definiert die alle einen übergebenen Wert verändern und die geänderte
-- Variante zurückliefern.
module System.ArrowVHDL.Circuit.Workers
where
-- Zur Funktionsdefinition werden Funktionen aus folgenden Modulen benötigt.
import Data.List (nub, (\\))
import GHC.Exts (sortWith)
import System.ArrowVHDL.Circuit.Descriptor
import System.ArrowVHDL.Circuit.Sensors
import System.ArrowVHDL.Circuit.Tests
-- \subsection{CircuitDescriptor Funktionen}
-- Dieser Abschnitt befasst sich mit Funktionen, die auf \hsSource{CircuitDescriptor}en arbeiten.
-- Mit der Funktion \hsSource{alterCompIDs} lassen sich alle Komponenten IDs innerhalb eines \hsSource{CircuitDescriptor}s verändern. Der erste
-- Parameter legt dabei die kleinst mögliche ID fest.
alterCompIDs :: Int -> CircuitDescriptor -> CircuitDescriptor
alterCompIDs i sg
= sg { nodeDesc = nd { nodeId = nodeId nd + i }
, nodes = map (alterCompIDs i) $ nodes sg
, edges = map (\ (MkEdge (ci,pi) (co,po))
-> (MkEdge (maybe ci (Just.(+i)) $ ci ,pi)
(maybe co (Just.(+i)) $ co ,po))
) $ edges sg
}
where nd = nodeDesc sg
-- Die Funktion \hsSource{dropCircuit} ermöglicht es, einzelne Schaltkreis-Beschreibungen aus dem \hsSource{CircuitDescriptor} zu entfernen.
-- Hierzu wird eine match-Funktion als erster Parameter erwartet.
dropCircuit :: (CircuitDescriptor -> Bool) -> CircuitDescriptor -> CircuitDescriptor
dropCircuit f sg
= sg { nodes = newNodes
, edges = newEdges
}
where specific = filter f (nodes sg)
newEdges = foldl (flip dropEdgesBordering) (edges sg) (map (nodeId.nodeDesc) specific)
newNodes = map (dropCircuit f) $ nodes sg \\ specific
-- flatten} ist eine Funktion, welche die interne Struktur des \hsSource{CircuitDescriptor}s \begriff{glättet}. Jeder
-- CircuitDescriptor} der nicht Atomar ist, enthält weitere Unterstrukturen. Diese beschreiben, woraus dieser
-- CircuitDescriptor} aufgebaut wird. Enthält der \hsSource{CircuitDescriptor} unnötige Verschachtelungen, werden diese mittels
-- flatten} entfernt.
-- Als Sonderfall gelten die Schaltungen, die Schleifen darstellen. Hier gibt es keine überflüssigen Verschachtelungen, mindestens aber muss
-- der Algorithmus zum erkennen solcher ein anderer sein, so dass \hsSource{flatten} auf diese Teilbereiche zunächst nicht angewandt werden
-- sollte.
flatten :: CircuitDescriptor -> CircuitDescriptor
flatten g
| isLoop g
= g
| otherwise
= g { nodes = nub $ atomCIDs
, edges = esBetweenAtoms
}
where atomCIDs = filter isAtomic $ allCircuits g
esFromAtoms = concat $ map (fromCompEdges g . nodeId . nodeDesc) atomCIDs
esFromOuter = filter isFromOuter $ edges g
esBetweenAtoms = zipWith MkEdge (map sourceInfo $ esFromOuter ++ esFromAtoms) (map nextAtomOrOut $ esFromOuter ++ esFromAtoms)
nextAtomOrOut = (\e -> let (c, p) = nextAtomic g e
in if c == mainID then (Nothing, p) else (Just c, p))
mainID = nodeId.nodeDesc $ g
-- Die Funktionen \hsSource{dropGenerated} sowie \hsSource{dropID} stellen Spezialfälle der \hsSource{dropCircuit} Funktion dar.
-- dropGenerated} löscht sämtliche \hsSource{CircuitDescriptor}en, die automatisch generiert wurden. Ebenso löscht \hsSource{dropID}
-- CircuitDescriptor}en, die den \hsSource{isID}-Test bestehen. \hsSource{isID} sowie \hsSource{isGenerated} sind im Modul
-- \ref{mod:Circuit.Tests} beschrieben.
dropGenerated :: CircuitDescriptor -> CircuitDescriptor
dropGenerated = dropCircuit isGenerated
dropID :: CircuitDescriptor -> CircuitDescriptor
dropID = dropCircuit isID
-- Diese Funktionen arbeiten auf \hsSource{CircuitDescriptor}en, und erzeugen Kanten, oder es handelt sich um Funktionen, die aus bestehenden
-- Kanten neue generieren.
-- Mit der Funktion \hsSource{connectCID} lassen sich zwei \hsSource{CircuitDescriptor}en miteinander verbinden. Dabei werden zwei
-- \hsSource{CircuitDescriptor}en übergeben, sowie die Quell-Komponenten ID und die Ziel-Komponenten ID zusammen mit einer Ziel-Pin ID. Erzeugt
-- wird eine Kante, welche die Verbindung zwischen beiden \hsSource{CircuitDescriptor}en darstellt. Von der Quelle wird keine \hsSource{PinID}
-- benötigt, da hier auf den nächst freien Pin zurückgegriffen wird. Auf der Ziel-Seite ist es dann aber notwendig, einen Ziel-Pin zu
-- definieren.
connectCID :: CircuitDescriptor -> CircuitDescriptor -> CompID -> (CompID, PinID) -> Edge
connectCID old_g g cidF (cidT,pidT)
= MkEdge (Just cidF, nextFpin) (Just cidT, pidT)
where nextFpin = head $ drop cntEsFrom $ sources.nodeDesc $ getComp old_g cidF
cntEsFrom = length $ filter (\x -> (not.isFromOuter $ x) && (srcComp x == cidF)) $ edges g
-- Zum entfernen von Kanten die an eine Komponente angrenzen, ist die Funktion \hsSource{dropEdgesBordering} da. Übergeben wird die ID der
-- Komponente, die heraus gelöst werden soll, sowie die Liste mit den betroffenen Kanten. Es wird dann eine neue List mit Kanten erstellt, die
-- nicht mehr zu der Komponente mit der besagten ID führen. Alle Kanten die nicht mehr an einer Komponente andocken, werden zusammengefügt.
-- Diese Funktion kann nur dann funktionieren, wenn die zu lösende Komponente genausoviele eingehende Pins, wie ausgehende Pins besitzt.
-- %%% TODO : Components with different InPinCount and OutPinCount have a PROBLEM
dropEdgesBordering :: CompID -> [Edge] -> [Edge]
dropEdgesBordering cid es
= (es ++ mergeEdges (toIt, fromIt)) \\ (toIt ++ fromIt)
where toIt = filter ((== (Just cid)).fst.sinkInfo) $ es
fromIt = filter ((== (Just cid)).fst.sourceInfo) $ es
-- \hsSource{mergeEdges} ist eine Funktion, die zwei Listen mit Kanten entgegennimmt und diese beiden zusammenfasst. Kanten die auf einen Pin
-- enden und Kanten die vom gleichen Pin starten, werden zu einer Kante zusammengefasst.
mergeEdges :: ([Edge], [Edge]) -> [Edge]
mergeEdges (xs, ys)
= zipWith (\x y -> MkEdge (sourceInfo x) (sinkInfo y)) xs' ys'
where x_snkPins = map snkPin xs
y_srcPins = map srcPin ys
xs' = sortWith snkPin $ filter (\edg -> (snkPin edg) `elem` y_srcPins) xs
ys' = sortWith srcPin $ filter (\edg -> (srcPin edg) `elem` x_snkPins) ys
-- Mit der \hsSource{fillEdgeInfoCompID} Funktion, lassen sich die Quell- und Ziel-Komponenten IDs in Kanten setzen, in denen bis dahin
-- \hsSource{Nothing} als Wert gespeichert ist. Dies ist dann notwendig, wenn eine neue Komponente in eine bestehende Struktur eingefügt wird.
-- Dies wird dann benötigt, wenn eine Komponente in eine Struktur eingefügt werden soll. Eine noch nicht integrierte Komponente bekommt ihre %% TODO : Werte ist sicher nicht das richtige wort hier
-- Werte von einer unbekannte Komponente (\hsSource{Nothing}) und liefert die Ergebnisse auch an \hsSource{Nothing}. Wird sie nun eine
-- Unterkomponente, so kann das \hsSource{Nothing} durch eine tatsächliche Komponenten ID ersetzt werden.
fillEdgeInfoCompID :: CompID -> Edge -> Edge
fillEdgeInfoCompID cid (MkEdge (Nothing, srcPid) (snkInfo)) = (MkEdge (Just cid, srcPid) (snkInfo))
fillEdgeInfoCompID cid (MkEdge (srcInfo) (Nothing, snkPid)) = (MkEdge (srcInfo) (Just cid, snkPid))
fillEdgeInfoCompID _ e = e
-- Ein ähnliches Problem wie \hsSource{fillEdgeInfoCompID} wird auch von den Funktionen \hsSource{fillSrcInfoCompID} und
-- \hsSource{fillSnkInfoCompID} gelöst. Diese unterscheiden sich lediglich darin, dass diese Funktionen jeweils nur die Quell-Pins oder nur die
-- Ziel-Pins betreffen.
fillSrcInfoCompID :: CompID -> Edge -> Edge
fillSrcInfoCompID cid (MkEdge (Nothing, srcPid) (snkCid, snkPid))
= (MkEdge (Just cid, srcPid) (snkCid, snkPid))
fillSnkInfoCompID :: CompID -> Edge -> Edge
fillSnkInfoCompID cid (MkEdge (srcCid, srcPid) (Nothing, snkPid))
= (MkEdge (srcCid, srcPid) (Just cid, snkPid))