OK, I've tried to get a reasonably compact version for testing.

There are 3 nim files: TESTmain, TESTmodule and Testlang.

  * Testmain
    * sets things up and creates a test text file to work with
    * does the loading and calling of TESTmodule
  * TESTmodule
    * 3 callable procs : initModule, speechModule, endModule
  * TESTlang
    * has procs for loading the text file into a Table and extracting from that 
Table


    
    
    # TESTmain : should load and call TESTmodule procs - crash on call to 
initModule
    # compile with : nim c -mm:orc --threads:on -d:useNimRtl TESTmain.nim
    # also tried without threads
    
    
    import os
    import strformat, strutils
    import std/times
    import dynlib
    import random
    
    import TESTdata
    
    
###################################################################################################
    
###################################################################################################
    # DYNAMIC LIB MODULE LOAD & CALL
    #
    # all modules only have 3 procs which have a standard call
    # - initModule (called with Settings, returns true or false)
    # - speechModule (called with SimState and returns SimState = SimData + 
speech queue)
    # - endModule void
    
###################################################################################################
    
###################################################################################################
    type
      InitModuleProc = proc(s: Settings): bool {.nimcall}
      SpeechModuleProc = proc(sd: SimState): SimState {.nimcall}
      EndModuleProc = proc() {.nimcall}
      # used .nimcall. as per https://forum.nim-lang.org/t/1400 that got me 
farthest on my first tries
      # plus, since it's the default for a nim proc and all is pure nim, this 
seems the optimal choice
    
    var
      libModule:  LibHandle
      
      initModuleAddr: pointer
      initModule: InitModuleProc
      
      speechModuleAddr: pointer
      speechModule: SpeechModuleProc
      
      endModuleAddr: pointer
      endModule: EndModuleProc
      
      moduleFile: string
      moduleLoaded: string = ""
      moduleAvailable: bool = false
      
      
      simstate: SimState
    
    proc loadModule(moduleName: string): bool =
      result = false
      # create path to module (working dir if development version)
      when defined(release):
        moduleFile = os.joinPath(pathBase, "Modules", moduleName & ".dll")
      else:
        moduleFile = moduleName & ".dll"
      
      debugEcho(fmt("Loading module file {moduleFile:s}"))
      # try loading the module
      try:
        libModule = loadLib(moduleFile)
        result = true
      except:
        debugEcho(fmt("ERROR: could not load module {moduleName}."))
        sleep(250)
        result = false
      
      # get address(es) of the proc(s) in the dynamic lib
      var moduleErrors: string = ""
      if libModule != nil:
        debugEcho(fmt("Loaded {moduleName} module."))
        initModuleAddr = libModule.symAddr("initModule")
        speechModuleAddr = libModule.symAddr("speechModule")
        endModuleAddr = libModule.symAddr("endModule")
        
        if initModuleAddr != nil:
          initModule = cast[InitModuleProc] (initModuleAddr)
        else:
          moduleErrors = moduleErrors & ("- initModule not found\n")
        if speechModuleAddr != nil:
          speechModule = cast[SpeechModuleProc] (speechModuleAddr)
        else:
          moduleErrors = moduleErrors & ("- speechModule not found\n")
        if endModuleAddr != nil:
          endModule = cast[EndModuleProc] (endModuleAddr)
        else:
          moduleErrors = moduleErrors & ("- endModule not found\n")
      else:
          moduleErrors = moduleErrors & ("- something wrong with module")
      
      if moduleErrors > "":
        debugEcho(fmt("ERRORS: loading {moduleName} gave problem(s):\n") & 
moduleErrors)
        result = false
      
      if result:  # seems to have loaded fine : init
        moduleLoaded = moduleName
      else:
        moduleLoaded = ""
      return result
    
    proc unloadModule() =
        if moduleLoaded != "":
          endModule()
          unloadLib(libModule)
    
    
    proc projectModule(simstate: SimState): SimState =
      
      if simstate.simdat.projectName == "":
        debugEcho(fmt("WARNING: No project {simstate.simdat.projectName}."))
        return    # nothing to do  TODO: default module, so speech enabled for 
debugging ?
      
      var
        sims = simstate # copy to modify
      
      debugEcho(fmt("In projectModule {sims.simdat.projectName} 
(loaded={moduleLoaded}).--------------------------------------------------"))
      
      # not same lib module: unload old and load correct one
      if moduleLoaded != sims.simdat.projectName:
        unloadModule()
        if loadModule(sims.simdat.projectName):
          debugEcho(fmt("Module {moduleLoaded} loaded successfully..."))
          # set data needed for initialization of module
          var settings: Settings
          # set some data for testing
          settings.pathFile = "C:/Users/ivan"
          settings.pathData = "C:/Users/ivan/Data"
          settings.aBool = true
          settings.anInt = 1504
          settings.aFloat = 15.04
          settings.aString = "Nim dll test"
          settings.lang = "en"
          debugEcho("Calling initModule with:")
          debugEcho(settings)
          discard initModule(settings)            # initialize !
          debugEcho("Returned from call to initModule")
        else:
          debugEcho(fmt("WARNING: Could not load the project 
{sims.simdat.projectName} module: {moduleFile}."))
      
      if libModule != nil:
        try:
          sims = speechModule(sims)
          debugEcho(fmt("Returned from speechModule (with {sims.speakQ.len} 
sentences)"))
        except:
          debugEcho("ERROR: Call to project module failed!")
      else:
        debugEcho(fmt("ERROR: Module not loaded or defined! ({moduleFile})"))
      return sims
    
    proc createTestfile() =
      # create a 500 line test text file
      var txt: string
      for i in 1..500:
        let r = rand(i)
        txt = txt & (fmt("TXT_{i:3d}:Random {(r)} out of {i}.\n"))
        if i in @[7, 12, 33, 88, 100, 105, 161, 163, 200, 300, 400, 488]:
          txt = txt & (fmt("// Just comment {i}\n"))  # fake some comment in 
the file
      try:
        writeFile("TEST-en.txt", txt)
      except:
        debugEcho("Could not write test file")
    
    proc main() =
      debugEcho("Creating test text file")
      createTestfile()
      simstate.simdat.projectName = "TESTmodule"
      debugEcho("Testing calls to speechModule")
      let testData = @["Ivan:Hi bro!:71", "Johan:Hi you:11", "Ivan:Are you 
coming?:7", "Johan:No, I'm going.:3", "Ma:Oh no!:1"]
      for i in 0..testData.high:
        let randomItem = rand(testData.high)
        simstate.simdat.actionlist.add( (0, testData[i]) )
        simstate.simdat.actionlist.add( (0, testData[(randomItem)]) )
        simstate = projectModule(simstate)
        debugEcho("Back from calling module with this to say:")
        for spk in simstate.speakQ:
          debugEcho(fmt("{spk.who:7s}|{spk.speaktime:8.1f}|{spk.sentence}"))
        sleep(1234)
    
    
    
    when isMainModule:
      main()
    
    
    Run
    
    
    # plugin to be loaded and called by TESTmain
    # FIRST create NimRtl.dll with "nim c -mm:orc -o:NimRtl.dll 
<path-to>NimRtl.nim
    # compile with : nim c -mm:orc -d:useNimRtl --noMain --app:lib 
TESTmodule.nim
    
    
    import os
    import std/times
    import strformat, strutils
    
    import TESTdata
    import TESTlang
    
    var
     data: int = 0
     timeNow: float
     timeStart: float = epochTime()
     moduleSettings: Settings
     sims: SimState
    
    
    proc processMessage(m: string) =
      debugEcho(fmt("Processing Message {m}"))
      let mesg = m.split(":")
      if mesg.len == 3:  # otherwise invalid data expecting 
"<name>:<sentence>:<lookupkey>"
        var spk: SpeakData
        spk.who = mesg[0]
        spk.sentence = mesg[1] & " " & getTXT(mesg[2])
        spk.speaktime = epochTime() - timeStart + 2.0
        spk.expiretime = spk.speaktime + 10.0
        sims.speakQ.add(spk)
    
    
    proc processFile(f: string) =
      debugEcho(fmt("File to process = {f}"))
    
    
    proc NimMain() {.nimcall, importc.}
    
    
    #{.push dynlib exportc.}
    
    proc initModule(settings: Settings): bool {.exportc, dynlib, nimcall.} =   
# also tried .dynlib, exportc.
      debugEcho("Arrived in initModule")
      NimMain()                  # Be sure to call this! It will set up garbage 
collector and initialize any global memory
      debugEcho("NimMain has been called")
      moduleSettings = settings  # copy to dll global var
      debugEcho("initModule finished")
      return true
    
    proc endModule() {.exportc, dynlib, nimcall.} =
      debugEcho("Closing down, these are the settings:\n", moduleSettings)
      GC_FullCollect()
    
    proc speechModule*(simstate: SimState): SimState {.exportc, dynlib, 
nimcall.} =
      sims = simstate            # copy to a global for this module
      
      # load project specific data
      loadSpeechData(simstate.simdat.projectName, "")
      
      let alist = sims.simdat.actionlist
      debugEcho(fmt("Action List contains {(sims.simdat.actionlist.len):02d} 
items"))
      # just iterate over (dummy) text file loaded with loadSpeechData
      for act in alist:
        moduleSettings.anInt += 1
        if act.what == ACT_MSG:
          debugEcho(fmt("- PROCESSING: {act.what:s}|{act.info}"))
          processMessage(act.info)
        elif act.what == ACT_FIL:
          debugEcho(fmt("- TO PROCESS: {act.what:s}|{act.info}"))
          processFile(act.info)
      
      # done processing
      sims.simdat.actionlist = @[]  # clear the actionlist
      return sims
    
    #{.pop}
    
    
    Run
    
    
    # Text data : can load different languages, but now only en English is 
supplied
    import times
    import tables
    import strformat
    import strutils
    import os
    #import zippy/ziparchives
    #import random
    
    
    var
      # lookup table with simple speech (words or sentences) found in 
data-<language> currently only 'en' is implemented
      # in data file starts with TXT_ to extract to this table
      textTable* = initOrderedTable[string, string]()
      countR, countT: int
      index: string
    
    proc uncomment*(s: string): string =
      # strip comments (start with //) from string
      var commentpos = s.find("//")
      if commentpos == 0:   # found at start
         return ""
      elif commentpos > 0:
         return s[0..commentpos-1].strip()
      else:
         return s
    
    
    proc loadSpeechData*(project:string = "", folder: string, language: string 
= "en") =
      # project is not used in this TEST version, just fixed TEST-en.txt (later 
to become <project>-en.txt)
      debugEcho("---------------------------------------In loadSpeechData")
      var
        speechData: string = ""
        resourceFilename: string = "TEST-en.txt"
      
      
      
      debugEcho(fmt("Loading speech data from {resourceFilename}\n"))
      # read in the data file from the zipped data file (TODO : depending on 
language)
      
      # Open the file (no zip for TEST)
      try:
        speechData = readFile(resourceFilename)
      except:
        debugEcho(fmt("- ERROR : could not find speech data file 
{resourceFilename}\n"))
      
      debugEcho("---------------------------------------")
      debugEcho("DUMP speechData:")
      debugEcho(" - size:   ", (sizeof(speechData)))
      debugEcho(" - length: ", (speechData.len))
      debugEcho(" - lines:  ", (speechData.countLines))
      debugEcho(speechData)
      
      # clear the data
      textTable.clear
      
      for d in speechData.splitLines():
        inc countR
        let dataLine = d.uncomment
        debugEcho(fmt("READ {countR:4d}|{dataLine}"))
        
        if dataLine.startsWith("TXT_"):
          # simple text (words or phrases)
          let s = dataLine.split(':')
          if s.len > 1:
             index = s[0].replace("TXT_", "")
             if not textTable.hasKey(index):
                # does not exist : create one
                textTable[index] = s[1]
                inc countT
      debugEcho(fmt("- checked {countR-1:d} items and used {countT}."))
      return
    
    
    proc getTXT*(index: string): string =
      # get text from textTable
      if textTable.hasKey(index):
        return textTable[index]
      else:
        # returned string may sound weird in TTS, but otherwise tracing what 
goes wrong will be very difficult
        return fmt("|{index} not found| ")
    
    
    Run

My tries two days ago got me a crash when iterating over the loaded text file. 
Now I don't even get that far.
    
    
    TESTmain
    Creating test text file
    In projectModule TESTmodule 
(loaded=).--------------------------------------------------
    Loading module file TESTmodule.dll
    Loaded TESTmodule module.
    Module TESTmodule loaded successfully...
    Calling initModule with:
    (pathFile: "C:/Users/ivan", pathData: "C:/Users/ivan/Data", lang: "en", 
aBool: true, anInt: 1504, aFloat: 15.04, aString: "Nim dll test")
    Traceback (most recent call last)
    C:\Users\Ivan\GFR\TESTmain.nim(178) TESTmain
    C:\Users\Ivan\GFR\TESTmain.nim(169) main
    C:\Users\Ivan\GFR\TESTmain.nim(132) projectModule
    SIGSEGV: Illegal storage access. (Attempt to read from nil?)
    
    
    Run

Reply via email to