XRWS         ! iH  U @B  $±ext_01.cat ext_01.dat readme.txt md/debugging.xml 1023 1452175248 df313f0b126942f39361fbf9e14a5b59
md/mainmenu.xml 1373 1456845673 c66888509a9886e16c44bf86972e437e
md/npc_staff.xml 1564 1456227691 0cc57ff08df653132945d4588a26fd3d
md/st_prod.xml 11602 1456412999 c5a55e2f8f4d5972cf993708ec67f7d4
t/0001-L007.xml 787 1455710955 d6d7a68de7be193d6fc562383b05f526
t/0001-L033.xml 632 1455284946 3945efd751f4382ba7f68bbf0868bea3
t/0001-L034.xml 666 1455284997 6c0012351a38c6a2ba9e5c091b035db7
t/0001-L039.xml 625 1455285043 503d150d8d1824e8529d3538d1a49873
t/0001-L044.xml 579 1455278450 986d50b4f9371035ad6cf3296ab015fa
t/0001-L049.xml 609 1455285079 5e22731f83e3fe0853b3af7a5a7ea84f
t/0001.xml 579 1455284864 986d50b4f9371035ad6cf3296ab015fa
ui.xml 372 1456845005 b9c5664ef589215a75f9a7e5817bea3e
ui/ego_detailmonitorHelper.xml 250 1456845021 7094cd76c9f175301b375b4ea7a54710
ui/stationprod/config_menu_st_prod.lua 16467 1491493503 768e9ed2efd3b12bf8b1e702d5301281
ui/stationprod/limit_menu_st_prod.lua 14829 1491493553 4fbb246c61af3c2c456c4f3134b07d55
ui/stationprod/production_menu_st_prod.lua 30029 1491493662 8ce57278e9d7442b45ac8c6d4ed452ec
<?xml version="1.0" encoding="utf-8" ?>
<mdscript name="Debugging" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="md.xsd">

  <cues>
    <cue name="DumpParams" instantiate="true" namespace="this">
      <conditions>
        <event_cue_signalled/>
      </conditions>
      <actions>
        <set_value name="$Label" exact="event.param.{1}"/>
        <set_value name="$Param2" exact="event.param.{2}"/>
        <set_value name="$Param3" exact="event.param.{3}"/>
        <debug_text text="$Label + ' event.param2 ' + $Param2 + ' event.param3 ' + $Param3"/>
        <do_all exact="@$Param2.count" counter="$Counter">
          <debug_text text="$Counter + ' ' + $Param2.{$Counter} + ' ' + @$Param2.{$Counter}.name"/>
        </do_all>
        <do_all exact="@$Param3.count" counter="$Counter">
          <debug_text text="$Counter + ' ' + $Param3.{$Counter} + ' ' + @$Param3.{$Counter}.name"/>
        </do_all>
      </actions>
    </cue>
  </cues>

</mdscript>
<?xml version="1.0" encoding="utf-8"?>
<diff>
  <!--	MainMenu - around line 200 replace call to ProductionMenu with StationProduction_ProductionMenu  -->
  <replace sel="/mdscript[@name='MainMenu']['201 hook in Station_Production']/cues/cue[@name='SectionHandler']/actions/do_elseif[@value=&quot;event.param == 'gMain_objectProduction'&quot;]/open_conversation_menu[@menu='ProductionMenu']" >
    <open_conversation_menu menu="Station_Production" param="event.param2" param2="event.param3" />
  </replace>

  <!--	Section handling, etc	-->
  <add sel="//cue[@name='SectionHandler']['hook in Station_Prod_Limit']/actions">
    <do_elseif value="event.param == 'gMain_Station_Prod_Slider'">
      <open_conversation_menu menu="Station_Prod_Limit" param="event.param2" param2="event.param3" />
    </do_elseif>
  </add>

  <!--	Section handling, etc	-->
  <add sel="//cue[@name='SectionHandler']['hook in Station_Prod_Config']/actions">
    <do_elseif value="event.param == 'gMain_Station_Prod_Config'">
      <open_conversation_menu menu="Station_Prod_Config" param="event.param2" param2="event.param3" />
    </do_elseif>
  </add>

<!--  <add sel="//cue[@name='SectionHandler']['hook in Station_Prod_Config']/actions">
    <signal_cue_instantly cue="md.Debugging.DumpParams" param="[event.param, event.param2, event.param3]"/>
  </add>
-->
</diff>
<?xml version="1.0" encoding="utf-8"?>
<diff xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="rfc5261.xsd">
  <!--	NPC_Staff - around line 465 add in manager BlackBoard cleanup on cStaff_workhere -->
  <add sel="/mdscript[@name='NPC_Staff']['465 remove St_Prod_Data from BlackBoard']/cues/cue[@name='OnPlatformPopulation_Staff']/cues/cue[@name='SectionHandler']/actions/do_elseif[@value=&quot;event.param == 'cStaff_workhere'&quot;]/do_if[@value='$actor.type == entitytype.manager']/set_actor_account[@actor='$actor']" pos="before" >
    <debug_text text="'Clearing $St_Prod_Data from BlackBoard for ' + $actor.name"/>
    <remove_value name="$actor.$St_Prod_Data"/>
    <remove_value name="$actor.$St_Prod_DataKeys"/>
    <remove_value name="$actor.$St_Prod_Data_Debug"/>
  </add>

  <!--	NPC_Staff - around line 651 add in manager BlackBoard cleanup on OnDroneArrival -->
  <add sel="/mdscript[@name='NPC_Staff']['651 remove St_Prod_Data from BlackBoard']/cues/cue[@name='OnPlatformPopulation_Staff']/cues/cue[@name='OnDroneArrival']/actions/do_if[@value='$actor.container != player.primaryship and not @$actor.container.controlentity.{$actor.type}']/do_if[@value='$actor.type == entitytype.manager']/set_actor_account[@actor='$actor']" pos="before" >
    <debug_text text="'Clearing $St_Prod_Data from BlackBoard for ' + $actor.name"/>
    <remove_value name="$actor.$St_Prod_Data"/>
    <remove_value name="$actor.$St_Prod_DataKeys"/>
    <remove_value name="$actor.$St_Prod_Data_Debug"/>
  </add>
</diff>
<?xml version="1.0" encoding="utf-8" ?>
<mdscript name="St_Prod" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="md.xsd">
  <cues>
    <!-- $St_Prod_Data {  1  key.timestamp,
                          2  key.sequence,
                          3  key.stage,
                          4  key.prodbrake,
                          5  key.debug,
                          6  key.proddata.state,
                          7  key.proddata.cycletime,
                          8  key.proddata.cycleefficiency,
                          9  key.proddata.remainingcycletime,
                          10 key.proddata.prevremainingcycletime,
                          11 key.proddata.products,
                          12 key.proddata.presources,
                          13 key.proddata.sresources -->
    <cue name="St_Prod_RoundRobin" namespace="this" checkinterval="10s" instantiate="true">
      <actions>
        <set_value name="$StartTime" exact="player.age"/>
        <set_value name="$Throttled" exact="0"/>
        <set_value name="$GlobalDebug" exact="0"/>
        <do_if value="@player.entity.$St_Prod_Data_Debug">
          <set_value name="$GlobalDebug" exact="@player.entity.$St_Prod_Data_Debug"/>
        </do_if>
        <!--<debug_text text="'Global Debug: ' + $GlobalDebug"/>-->
        <find_object name="$Stations" class="class.station" space="player.galaxy" owner="faction.player" multiple="true"/>
        <do_if value="$GlobalDebug gt 0"> <debug_text text="'Starting sweep:'"/> </do_if>
        <do_all exact="$Stations.count" counter="$Counter">
          <set_value name="$Manager" exact="@$Stations.{$Counter}.tradenpc"/>
          <do_if value="$Manager.$St_Prod_Data? and $Manager.$St_Prod_Data != '' ">
            <set_value name="$StationDebug" exact="0"/>
            <do_if value="@$Manager.$St_Prod_Data_Debug">
              <set_value name="$StationDebug" exact="@$Manager.$St_Prod_Data_Debug"/>
            </do_if>
            <!--<debug_text text="'Station Debug: ' + $StationDebug"/>-->
            <set_value name="$EffectiveDebug" exact="$StationDebug"/>
            <do_if value="$StationDebug lt $GlobalDebug">
              <set_value name="$EffectiveDebug" exact="$GlobalDebug"/>
            </do_if>
            <do_if value="$EffectiveDebug gt 0">
              <debug_text text="$Stations.{$Counter}.name + ' - ' + $Stations.{$Counter}.zone.name + '. Effective Debug: ' + $EffectiveDebug"/>
            </do_if>
            <create_list name="$Monitored" comment="list of monitored prod modules"/>
            <do_all exact="@$Manager.$St_Prod_DataKeys.count" counter="$CounterPrK">
              <set_value name="$Throttled" operation="add"/>
              <set_value name="$Key" exact="'$' + $Manager.$St_Prod_DataKeys.{$CounterPrK}"/>
              <do_all exact="$Stations.{$Counter}.productions.count" counter="$CounterP" >
                <set_value name="$PModule" exact="$Stations.{$Counter}.productions.{$CounterP}"/>
                <set_value name="$PModuleSequence" exact="$PModule.sequence"/>
                <do_if value="$PModule.sequence == ''" comment="turn ['',0] into [a,0]">
                  <set_value name="$PModuleSequence" exact="'a'"/>
                </do_if>
                <do_if value="($PModuleSequence == $Manager.$St_Prod_Data.{$Key}.{2}) and
                              ($PModule.stage == $Manager.$St_Prod_Data.{$Key}.{3})" comment="match prod module">
                  <set_value name="$uid" exact="'%1_%2_%3'.[$PModuleSequence, $PModule.stage, $Manager.$St_Prod_Data.{$Key}.{11}.{1}.{'$ware'}]"/>
                  <do_if value="$EffectiveDebug gt 2">
                    <debug_text text="'uid: ' + $uid + ' key: ' + $Manager.$St_Prod_DataKeys.{$CounterPrK}"/>
                  </do_if>
                  <set_value name="$ProdStatus" exact="$Manager.$St_Prod_Data.{$Key}.{6}"/>
                  <do_if value="$uid != $Manager.$St_Prod_DataKeys.{$CounterPrK}">
                    <set_value name="$ProdStatus" exact="'producing another ware'"/>
                  </do_if>
                  <do_if value="$EffectiveDebug gt 1"> 
                    <debug_text text="'\tMatched: ' + $PModule.name  + ' ' + $Key + ' - ' + $ProdStatus"/>
                  </do_if>
                  <set_value name="$RemainingCycletime" exact="($Manager.$St_Prod_Data.{$Key}.{9})f"/>
                  <set_value name="$PrevRemainingCycletime" exact="($Manager.$St_Prod_Data.{$Key}.{10})f"/>
                  <do_if value="($Manager.$St_Prod_Data.{$Key}.{6} =='producing') and 
                                ($uid == $Manager.$St_Prod_DataKeys.{$CounterPrK}) and
                                ($RemainingCycletime > $PrevRemainingCycletime) " comment="its producing the same product">
                    <set_value name="$results" exact="'\tRestocking primary resources - ' + $Stations.{$Counter}.name + '.'"/>
                    <do_all exact="@$Manager.$St_Prod_Data.{$Key}.{12}.count" counter="$CounterRes" comment="return primary resources">
                      <set_value name="$Res" exact="$Manager.$St_Prod_Data.{$Key}.{12}.{$CounterRes}"/>
                      <set_value name="$Amount"  exact="($Res.{'$cycle'} * $Manager.$St_Prod_Data.{$Key}.{4})i"/>
                      <set_value name="$results" exact="$results + ' ' + $CounterRes + ': ' + $Res.{'$ware'} + ' - ' + $Amount + '/' + $Res.{'$cycle'}"/>
                      <add_cargo object="$Stations.{$Counter}" ware="ware.{$Res.{'$ware'}}" exact="$Amount" />
                    </do_all>
                    <do_if value="$EffectiveDebug gt 1"> <debug_text text="$results"/> </do_if>
                    <do_if value="$Manager.$St_Prod_Data.{$Key}.{13}.count?">
                      <set_value name="$results" exact="'\tRestocking secondary resources - ' + $Stations.{$Counter}.name + '.'"/>
                      <do_all exact="$Manager.$St_Prod_Data.{$Key}.{13}.count" counter="$CounterRes" comment="return primary resources">
                        <set_value name="$Res" exact="$Manager.$St_Prod_Data.{$Key}.{13}.{$CounterRes}"/>
                        <set_value name="$Amount"  exact="($Res.{'$cycle'} * $Manager.$St_Prod_Data.{$Key}.{4})i"/>
                        <set_value name="$results" exact="$results + ' ' + $CounterRes + ': ' + $Res.{'$ware'} + ' - ' + $Amount + '/' + $Res.{'$cycle'}"/>
                        <add_cargo object="$Stations.{$Counter}" ware="ware.{$Res.{'$ware'}}" exact="$Amount" />
                      </do_all>
                      <do_if value="$EffectiveDebug gt 1"> <debug_text text="$results"/> </do_if>
                    </do_if>
                    <set_value name="$PriWare" exact="$Manager.$St_Prod_Data.{$Key}.{11}.{1}"/>
                    <do_if value="$EffectiveDebug gt 1">
                      <debug_text text="'\tPolling delay: %1s'.[player.age - $Manager.$St_Prod_Data.{$Key}.{1}]"/>
                    </do_if>
                    <set_value name="$AdjRemCycletime" exact="$RemainingCycletime + $Manager.$St_Prod_Data.{$Key}.{1} - player.age" 
                               comment="Compensate for delay between GetProductionModuleData call result in lua 
                                        and it being processed by md script"/>
                    <do_if value="$AdjRemCycletime lt 0">
                      <set_value name="$AdjRemCycletime" exact="0.1"/>
                    </do_if>
                    <set_value name="$ShowStockAdj" exact="false"/>
                    <do_if value="$EffectiveDebug gt 1">
                      <debug_text text="'\tSchedule stock correction of %3/%4 %1 in %2s'.[$PriWare.{'$ware'}, $AdjRemCycletime,
                                                                             ($PriWare.{'$cycle'} * $Manager.$St_Prod_Data.{$Key}.{4})i, 
                                                                             $PriWare.{'$cycle'}]"/>
                      <set_value name="$ShowStockAdj" exact="true"/>
                    </do_if>
                    <signal_cue_instantly cue="St_Prod_Recycle" param="[$AdjRemCycletime, $PModule, $PriWare.{'$ware'}, 
                                                                        $PriWare.{'$cycle'}, $Manager.$St_Prod_Data.{$Key}.{4},
                                                                        $ShowStockAdj]"/>
                  </do_if>
                  <set_value name="$BuildStage" exact="'%1_%2'.[$PModuleSequence, $PModule.stage]"/>
                  <do_if value="not $Monitored.indexof.{$BuildStage}" comment="only run one monitor per prod module">
                    <append_to_list name="$Monitored" exact="$BuildStage"/>
                    <raise_lua_event name="'St_Prod_RemainingCycletime'" param="$PModule" comment="update module info"/>
                  </do_if>
                  <break/>
                </do_if>
              </do_all>
            </do_all>
            <do_if value="$EffectiveDebug gt 1">
              <debug_text text="'\tMonitoring ' + $Monitored"/>
            </do_if>
          </do_if>
        </do_all>
        <do_if value="$GlobalDebug gt 0"> 
          <debug_text text="'Found %1 Stations in %2ms. Prods braked %3. Cycle 10s'.[$Stations.count, (player.age - $StartTime)ms, $Throttled]"/>
        </do_if>
      </actions>
    </cue>

    <cue name="St_Prod_Recycle" namespace="this" instantiate="true" comment="Remove product at the end of the production cycle">
      <conditions>
        <event_cue_signalled/>
      </conditions>
      <delay exact="(event.param.{1})s"/>
      <actions>
        <set_value name="$PModule" exact="event.param.{2}"/>
        <set_value name="$Ware" exact="event.param.{3}"/>
        <set_value name="$Amount" exact="event.param.{4}"/>
        <set_value name="$Brake" exact="event.param.{5}"/>
        <set_value name="$ShowStockAdj" exact="event.param.{6}"/>
        <do_if value="$ShowStockAdj">
          <debug_text text="'\tCorrected stock level %1/%2 %3 for %4 - %5.'.[($Amount * $Brake)i, $Amount, $Ware, 
                                                                            $PModule.container.name, $PModule.container.zone.name]"/>
        </do_if>
        <add_cargo object="$PModule.container" ware="ware.{$Ware}" exact="(-1 * $Amount * $Brake)i" comment="remove primary ware" />
      </actions>
    </cue>

    <cue name="St_Prod_Logger" namespace="this" instantiate="true" comment="Remote logger for lua scripts avoids using DebugError" >
      <conditions>
        <event_ui_triggered screen="'St_Prod_Logger'" />
      </conditions>
      <actions>
        <do_if value="event.param3 != null" comment="4.00 RC2 or later">
          <debug_text text="event.param3"/>
        </do_if>
        <do_else>
          <debug_text text="event.param2" comment="earlier than 4.00 RC2"/>
        </do_else>
      </actions>
    </cue>

    <cue name="St_Prod_GetPlayer" namespace="this" instantiate="true" comment="finds the player object and signals it to lua scripts" >
      <conditions>
        <check_any>
          <event_ui_triggered screen="'St_Prod_GetPlayer'" />
        </check_any>
      </conditions>
      <actions>
        <debug_text text="'MD Player is ' + player.entity + ' ' + player.entity.name"/>
        <raise_lua_event name="'St_Prod_Player'" param="player.entity" comment="tell lua the player"/>
      </actions>
    </cue>
  </cues>
</mdscript>﻿<?xml version="1.0" encoding="UTF-8"?>
<!-- English v0.01	9th February 2016 alexalsp -->

<language>
	<page id="360003" title="StationProductionManager" descr="Texts for Station production manager." voice="no">
	     <t id="1">Ограничение производства</t>
	     <t id="10">Цыкл</t>
	     <t id="20">Настройка</t>
	     <t id="30">Глобальное</t>
	     <t id="40">Уровень отладки</t>
	     <t id="50">Активировать настройки отладки</t>
	     <t id="70">Записать лимит производства всех станций в  </t>
	     <t id="90">Без ограничений</t>
	     <t id="100">Производство уменьшено на  </t>
	</page>
</language>﻿<?xml version="1.0" encoding="UTF-8"?>
<!-- French v0.01	9th February 2016 jth -->

<language>
	<page id="360003" title="StationProductionManager" descr="Textes pour la station de directeur de production." voice="no">
	     <t id="1">Limitation de la production</t>
	     <t id="10">Cycle</t>
	     <t id="20">Configuration</t>
	     <t id="30">Mondial</t>
	     <t id="40">Niveau de débogagel</t>
	     <t id="50">Activer Paramètres Debug</t>
	     <t id="70">Ecrire limites de production à </t>
	     <t id="90">Aucune limite ensemble</t>
	     <t id="100">Réduire la production par </t>
	</page>
</language>﻿<?xml version="1.0" encoding="UTF-8"?>
<!-- Spanish v0.01	9th February 2016 jth -->

<language>
	<page id="360003" title="StationProductionManager" descr="Textos para la estación del director de producción." voice="no">
	     <t id="1">La limitación de la producción</t>
	     <t id="10">Ciclo</t>
	     <t id="20">Configuración</t>
	     <t id="30">Global</t>
	     <t id="40">Nivel de depuración</t>
	     <t id="50">Activar la configuración de depuración</t>
	     <t id="70">Escribe límites en la producción a </t>
	     <t id="90">No se ha establecido límite</t>
	     <t id="100">Reducir la producción en </t>
	</page>
</language>﻿<?xml version="1.0" encoding="UTF-8"?>
<!-- Italian v0.01	9th February 2016 jth -->

<language>
	<page id="360003" title="StationProductionManager" descr="Testi per Stazione direttore di produzione." voice="no">
	     <t id="1">Produzione Limitare</t>
	     <t id="10">Ciclo</t>
	     <t id="20">Configurazione</t>
	     <t id="30">Globale</t>
	     <t id="40">Livello di debug</t>
	     <t id="50">Attivare le impostazioni di debug</t>
	     <t id="70">Scrivi i limiti di produzione per </t>
	     <t id="90">Nessun limite impostato</t>
	     <t id="100">Ridurre la produzione per </t>
	</page>
</language>﻿<?xml version="1.0" encoding="UTF-8"?>
<!-- English v0.01	9th February 2016 jth -->

<language>
	<page id="360003" title="StationProductionManager" descr="Texts for Station production manager." voice="no">
	     <t id="1">Production Limiting</t>
	     <t id="10">Cycle</t>
	     <t id="20">Configuration</t>
	     <t id="30">Global</t>
	     <t id="40">Debug level</t>
	     <t id="50">Activate Debug Settings</t>
	     <t id="70">Write Production limits to </t>
	     <t id="90">No limit set</t>
	     <t id="100">Reduce production by </t>
	</page>
</language>﻿<?xml version="1.0" encoding="UTF-8"?>
<!-- German v0.01	9th February 2016 jth -->

<language>
	<page id="360003" title="StationProductionManager" descr="Texte für Station Produktionsleiter ." voice="no">
	     <t id="1">Produktionsbegrenzung</t>
	     <t id="10">Zyklus</t>
	     <t id="20">Konfiguration</t>
	     <t id="30">Weltweit</t>
	     <t id="40">Debug-Level</t>
	     <t id="50">Aktiviere Debug-Einstellungen</t>
	     <t id="70">Schreiben Produktionsgrenzen zu </t>
	     <t id="90">Keine Begrenzung Satz</t>
	     <t id="100">Reduzieren Produktion von </t>
	</page>
</language>﻿<?xml version="1.0" encoding="UTF-8"?>
<!-- English v0.01	9th February 2016 jth -->

<language>
	<page id="360003" title="StationProductionManager" descr="Texts for Station production manager." voice="no">
	     <t id="1">Production Limiting</t>
	     <t id="10">Cycle</t>
	     <t id="20">Configuration</t>
	     <t id="30">Global</t>
	     <t id="40">Debug level</t>
	     <t id="50">Activate Debug Settings</t>
	     <t id="70">Write Production limits to </t>
	     <t id="90">No limit set</t>
	     <t id="100">Reduce production by </t>
	</page>
</language><?xml version="1.0" encoding="UTF-8"?>
<addon name="stationproduction">
  <environment type="detailmonitor">
    <file name="ui/stationprod/production_menu_st_prod.lua" />
    <file name="ui/stationprod/limit_menu_st_prod.lua" />
    <file name="ui/stationprod/config_menu_st_prod.lua" />
    <dependency name="ego_detailmonitorHelper" />
  </environment>
</addon><addon name="ego_detailmonitorHelper" viewtype="detailmonitor">
  <file name="ui/stationprod/production_menu_st_prod.lua" />
  <file name="ui/stationprod/limit_menu_st_prod.lua" />
  <file name="ui/stationprod/config_menu_st_prod.lua" />
</addon>local menu = {
	name = "Station_Prod_Config",
}

local function init()
	Menus = Menus or { }
	table.insert(Menus, menu)
	if Helper then
		Helper.registerMenu(menu)
	end
    RegisterEvent("St_Prod_Player", menu.St_Prod_Player)
end

function menu.cleanup()
	menu.title = nil

	menu.infotable = nil
	menu.selecttable = nil
	
	menu.priware = nil
	menu.module = nil
	menu.manager = nil
	menu.globaldebug = nil
	menu.globaldebugN = nil
	menu.stationdebug = nil
	menu.stationdebugN = nil
	menu.moduledebug = nil
	menu.moduledebugN = nil
	menu.modulelimited = nil
end

-- Menu member functions

function menu.onShowMenu()
	menu.priware = menu.param[3]
	menu.module = menu.param[4]
	menu.title = ReadText(360003, 1) .. " - " .. ReadText(360003, 20)
	menu.moduledebug = 2
  
	-- get the global debug level from player BlackBoard
	local playership = GetPlayerPrimaryShipID()
    if playership then
		local player = GetComponentData(playership, "controlentity")
		if player then 
			menu.player = player
		end
	end

	if menu.player then 
		menu.globaldebug = GetNPCBlackboard(menu.player, "$St_Prod_Data_Debug") or 0		
	else
		menu.globaldebug = 0
	end
	menu.globaldebugN = menu.globaldebug

 	-- get the station debug level from manager BlackBoard
	local station = GetComponentData(menu.module, "parent")
	if station then
		menu.manager = GetComponentData(station, "tradenpc")
	end
	menu.stationdebug = 0		
	if menu.manager then
		menu.stationdebug = GetNPCBlackboard(menu.manager, "$St_Prod_Data_Debug") or 0
	end
	menu.stationdebugN = menu.stationdebug
	
	-- get the module debug level from manager BlackBoard
	local moduid = menu.getuid()

	if menu.manager then
		local mdproddata = GetNPCBlackboard(menu.manager, "$St_Prod_Data") or {}
		if mdproddata[moduid] then 
			menu.moduledebug = mdproddata[moduid][5] or 0
			menu.modulelimited = true
		else 
			menu.moduledebug = "_"
			menu.modulelimited = false
		end
	end

	local setup = Helper.createTableSetup(menu)
	setup:addTitleRow({
		Helper.createFontString(menu.title, false, "left", 255, 255, 255, 100, Helper.headerRow1Font, Helper.headerRow1FontSize, false, Helper.headerRow1Offsetx, Helper.headerRow1Offsety, Helper.headerRow1Height, Helper.headerRow1Width)
	})
	setup:addTitleRow({ 
		Helper.createFontString("", false, "left", 129, 160, 182, 100, Helper.headerRow2Font, Helper.headerRow2FontSize, false, Helper.headerRow2Offsetx, Helper.headerRow2Offsety, Helper.headerRow2Height, Helper.headerRow1Width)	-- text depends on selection
	})
	local infodesc = setup:createCustomWidthTable({0}, false)

	setup = Helper.createTableSetup(menu)
	-- Debug level
	setup:addHeaderRow({
		ReadText(360003, 40)
		}, nil, {8})
	-- Global
	menu.addRadio(setup, ReadText(360003, 30) .. ReadText(1001, 120 ), {0,1,2,3}, menu.globaldebug)
	-- Station
	menu.addRadio(setup, ReadText(1001, 3) .. ReadText(1001, 120 ) .. " " .. GetComponentData(GetComponentData(menu.module,"parent"),"name"), {0,1,2,3}, menu.stationdebug)
	-- Production Module
	setup:addSimpleRow({
		Helper.getEmptyCellDescriptor()
	}, nil, {8})
	setup:addSimpleRow({
		Helper.getEmptyCellDescriptor(),
--		Helper.createFontString(ReadText(360003, 50), false, "right"),
		Helper.createFontString("", false, "right"),
		Helper.createButton(Helper.createButtonText(ReadText(1001, 14), "center", Helper.standardFont, Helper.standardFontSize, 255, 255, 255, 100), nil, false, true)
	}, nil, {1,6,1})

	setup:addFillRows(5, nil, {8})

	-- Write production limits to Logbook
	setup:addHeaderRow({ 
		ReadText(360003, 70) .. ReadText(1001, 5700)
		}, nil, {8})
	setup:addSimpleRow({
		Helper.getEmptyCellDescriptor(),
		ReadText(360003, 30) .. ReadText(1001, 120 ),
		Helper.createButton(Helper.createButtonText(ReadText(1001, 14), "center", Helper.standardFont, Helper.standardFontSize, 255, 255, 255, 100), nil, false, true)
	}, nil, {1,6,1})
	setup:addSimpleRow({
		Helper.getEmptyCellDescriptor(),
		ReadText(1001, 3) .. ReadText(1001, 120 ) .. " " .. GetComponentData(GetComponentData(menu.module,"parent"),"name"),
		Helper.createButton(Helper.createButtonText(ReadText(1001, 14), "center", Helper.standardFont, Helper.standardFontSize, 255, 255, 255, 100), nil, false, true)
	}, nil, {1,6,1})
	local text = ReadText(1001, 45) .. ReadText(1001, 120 ) .. " " .. GetComponentData(menu.module,"name") .. " - " .. GetWareData(menu.priware, "name")
	if not menu.modulelimited then
		text =  text  .. "\27Y - " .. ReadText(360003, 90) .. "\27X" 
	end
	setup:addSimpleRow({
		Helper.getEmptyCellDescriptor(),
		text,
		Helper.createButton(Helper.createButtonText(ReadText(1001, 14), "center", Helper.standardFont, Helper.standardFontSize, 255, 255, 255, 100), nil, false, menu.modulelimited)
	}, nil, {1,6,1})

	setup:addFillRows(10, nil, {7})

	-- Clear Production Limiting
	setup:addHeaderRow({ 
		ReadText(1001, 5706) .. " " .. ReadText(360003, 1)
		}, nil, {8})
	setup:addSimpleRow({
		Helper.getEmptyCellDescriptor(),
		ReadText(360003, 30) .. ReadText(1001, 120 ),
		Helper.createButton(Helper.createButtonText(ReadText(1001, 14), "center", Helper.standardFont, Helper.standardFontSize, 255, 255, 255, 100), nil, false, true)
	}, nil, {1,6,1})
	setup:addFillRows(13, nil, {7})
	setup:addSimpleRow({
		Helper.getEmptyCellDescriptor(),
		ReadText(1001, 3) .. ReadText(1001, 120 ) .. " " .. " - " .. GetComponentData(GetComponentData(menu.module,"parent"),"name"),
		Helper.createButton(Helper.createButtonText(ReadText(1001, 14), "center", Helper.standardFont, Helper.standardFontSize, 255, 255, 255, 100), nil, false, true)
	}, nil, {1,6,1})

	local selectdesc = setup:createCustomWidthTable({ 5 * Helper.standardTextOffsetx, 0, Helper.standardButtonWidth, Helper.standardButtonWidth, Helper.standardButtonWidth, Helper.standardButtonWidth, 2 * Helper.standardTextOffsetx, 15 * Helper.standardTextOffsetx }, false, false, true, 1, 0, 0, Helper.tableOffsety)

	-- create tableview
	menu.infotable, menu.selecttable = Helper.displayTwoTableView(menu, infodesc, selectdesc, false)

	-- set button scripts
	-- debug
	menu.addRadioButtons(2, 3, 4, "global")
	menu.addRadioButtons(3, 3, 4, "station")

	Helper.setButtonScript(menu, nil, menu.selecttable, 5, 8, function () return menu.debugApply() end)

	-- logbook
	Helper.setButtonScript(menu, nil, menu.selecttable, 7, 8, function () return menu.logbook("global") end)
	Helper.setButtonScript(menu, nil, menu.selecttable, 8, 8, function () return menu.logbook("station") end)
	if menu.modulelimited then
		Helper.setButtonScript(menu, nil, menu.selecttable, 9, 8, function () return menu.logbook("module") end)
	end

	-- clear
	Helper.setButtonScript(menu, nil, menu.selecttable, 12, 8, function () return menu.clear("global") end)
	Helper.setButtonScript(menu, nil, menu.selecttable, 14, 8, function () return menu.clear("station") end)

	-- clear descriptors again
	Helper.releaseDescriptors()
end

-- menu.updateInterval = 2.0
function menu.addRadio(setup, text, buttons, value)
	-- menu.remotelog("addRadio: '" .. tostring(text) .. "' " .. tostring(#buttons) .. " " .. tostring(value))
	local radio = {Helper.getEmptyCellDescriptor(), text}
	for button = 1, #buttons, 1 do
		table.insert(radio, Helper.createButton(Helper.createButtonText(buttons[button], "center", Helper.standardFont, Helper.standardFontSize, 255, 255, 255, 100), nil, false, true))
	end
	table.insert(radio, Helper.getEmptyCellDescriptor())
	table.insert(radio, Helper.createFontString(value, false, "center"))
	setup:addSimpleRow(radio)
end

function menu.addRadioButtons(row, col, numbuttons, scope)
	-- menu.remotelog("addRadioButtons: " .. tostring(row) .. "," .. tostring(col) .. "," .. tostring(numbuttons) .. " " .. tostring(scope))
	for buttoncol = col, col + numbuttons, 1 do
		Helper.setButtonScript(menu, nil, menu.selecttable, row, buttoncol, function () return menu.debug(row, buttoncol - col, scope) end)
	end
end

function menu.St_Prod_Player(eventname, player)
	menu.remotelog("Player from md: " .. eventname .. " , " .. tostring(player))
	menu.player = player
end

function menu.debugApply() -- apply the new debug settings
	Helper.updateCellText(menu.selecttable, 5, 2, "")

	if menu.player then 
		SetNPCBlackboard(menu.player, "$St_Prod_Data_Debug", menu.globaldebugN) -- set Global Debug level
		local globaldebug = GetNPCBlackboard(menu.player, "$St_Prod_Data_Debug") or ""		
		if menu.globaldebug ~= globaldebug then
			menu.remotelog("Global Debug - Before: " .. tostring(menu.globaldebug) .. " Now: " .. tostring(globaldebug))
			menu.globaldebug = globaldebug
		end
	else
		menu.remotelog("Player unknown")
	end
	
	if menu.manager then
		SetNPCBlackboard(menu.manager, "$St_Prod_Data_Debug", menu.stationdebugN) -- set Station Debug level
		local stationdebug = GetNPCBlackboard(menu.manager, "$St_Prod_Data_Debug") or ""		
		if menu.stationdebug ~= stationdebug then
			menu.remotelog("Station Debug - Before: " .. tostring(menu.stationdebug) .. " Now: " .. tostring(stationdebug))
			menu.stationdebug = stationdebug
		end
	end
end

function menu.getuid()
	-- get build stage and sequence to use as unique key for this prod module
	local sequence, stage = GetComponentData(menu.module, "sequence", "stage")
	if sequence and sequence == "" then 
		sequence = "a" 
	end
	local moduid = sequence .. "_" .. stage .. "_" .. menu.priware
	-- DebugError("Moduid is " .. moduid)
	return moduid
end

function menu.debug(row, value, scope)
	-- menu.remotelog("Debug: " .. tostring(row) .. "," .. tostring(value) .. " " .. tostring(scope))
	Helper.updateCellText(menu.selecttable, row, 8, value)
	Helper.updateCellText(menu.selecttable, 5, 2, "\27Y" .. ReadText(360003, 50) .. "\27X")
	--menu.remotelog(tostring(menu.globaldebug) .. " " .. tostring(menu.globaldebugN) .. " " .. tostring(value))
	--menu.remotelog(tostring(menu.stationdebug) .. " " .. tostring(menu.stationdebugN) .. " " .. tostring(value))
	if scope == "global" then
		menu.globaldebugN = value
	elseif scope == "station" then
		menu.stationdebugN = value
	elseif scope == "module" then
		menu.moduledebugN = value
	end
	if menu.globaldebug ~= menu.globaldebugN or menu.stationdebugN ~= menu.stationdebug then
		Helper.updateCellText(menu.selecttable, 5, 2, "\27Y" .. ReadText(360003, 50) .. "\27X")		
	else
		Helper.updateCellText(menu.selecttable, 5, 2, "")		
	end
end

function menu.logbook(scope)
	menu.remotelog("LogBook: " .. tostring(scope))
	if scope == "global" then
		local owner = GetComponentData(menu.player, "owner")
		if owner then
			local playerstations = GetContainedStationsByOwner(owner) or {}
			for _, station in ipairs(playerstations) do
				local manager = GetComponentData(station, "tradenpc") 
				if manager then
					menu.logbymanager(manager, station)
				end
			end
		else
			menu.remotelog("Logbook: production limiting - No player stations found")
		end
	elseif scope == "station" then
		local station = GetComponentData(menu.module, "parent")
		if station then
			menu.logbymanager(menu.manager, station)
		else
			menu.remotelog("Logbook: production limiting - Unknown station")
		end
	elseif scope == "module" then
		local mdproddata = GetNPCBlackboard(menu.manager, "$St_Prod_Data") or {}		
		local moduid = menu.getuid()
		if mdproddata[moduid] then
			local mdprodbrake = mdproddata[moduid][4]
			local priware = mdproddata[moduid][11][1]["name"]
			if mdprodbrake then
				menu.logbymodule(menu.module, priware, mdprodbrake)
			end
		end
	end
end

function menu.logbymanager(manager, station)
	if manager then
		local mdproddata = GetNPCBlackboard(manager, "$St_Prod_Data")		
		local keys = GetNPCBlackboard(manager, "$St_Prod_DataKeys")		
		if mdproddata then
			-- menu.remotelog("Iterating through BlackBoard for " .. GetComponentData(station, "name") .. ". entries: " .. #keys)
			if #keys > 0 then
				for _, key in ipairs(keys) do
					local proddata = mdproddata[key]
					if proddata then 
						local sequence = proddata[2]
						if sequence == "a" then 
							sequence = "" 
						end
						local stage = proddata[3]
						local mdprodbrake = proddata[4]
						local priware = proddata[11][1]["name"]
						local prodmodules = GetProductionModules(station) or {}
						for _, prodmod in ipairs(prodmodules) do
							if GetComponentData(prodmod, "sequence") and GetComponentData(prodmod, "sequence") == sequence and GetComponentData(prodmod, "stage") and GetComponentData(prodmod, "stage") == stage then
								if mdprodbrake then
									menu.logbymodule(prodmod, priware , mdprodbrake)
								end
								break
							end
						end
					end
				end
			end
		else
			-- menu.remotelog("menu.logbymanager - No $St_Prod_Data for " .. GetComponentData(station, "name"))
		end
	else
		-- menu.remotelog("menu.logbymanager - No manager")
	end
end

function menu.logbymodule(module, product, prodbrake)
	if menu.manager then -- do nothing without a manager
		local station = GetComponentData(module, "parent")
		if station then 
			local zone = GetComponentData(GetComponentData(station, "parent"), "name") or "Unknown zone"
			local stationdesc = (GetComponentData(station, "name") or "Unknown Station") .. " - " .. (GetComponentData(module, "name") or "Unknown Module") .. " " .. ReadText(1001, 2953) .. " " .. tostring(zone) .. ". "
			stationdesc = stationdesc .. ReadText(360003, 100) .. menu.stringf(prodbrake * 100) .. "%" .. " - " .. tostring(product) 
			AddLogbookEntry("general", stationdesc)
			menu.remotelog("Adding to logbook: " .. stationdesc)
		end
	end
end

function menu.clear(scope)
	menu.remotelog("Clear production limits: " .. tostring(scope))
	if scope == "global" then
		if menu.player then 
			local owner = GetComponentData(menu.player, "owner")
			if owner then
				local playerstations = GetContainedStationsByOwner(owner) or {}
				for _, station in ipairs(playerstations) do
					local manager = GetComponentData(station, "tradenpc") 
					if manager and GetNPCBlackboard(manager, "$St_Prod_Data") then
						menu.remotelog("Wiping the blackboard for " .. (GetComponentData(station,"name") or "Unknown Station"))
						SetNPCBlackboard(manager, "$St_Prod_Data", "") -- wipe the BlackBoard
						SetNPCBlackboard(manager, "$St_Prod_DataKeys", "") -- wipe the keys
					end
				end
			end
		else  -- find the player
			local version = tonumber(string.sub(GetVersionString(), string.find(GetVersionString(), '(',1,true) + 1, string.find(GetVersionString(), '(',1,true) + 6)) or 208022
			if version < 208022 then -- compensate for AddUITriggeredEvent parameter change
				AddUITriggeredEvent("St_Prod_GetPlayer", "")
			else
				AddUITriggeredEvent("St_Prod_GetPlayer", "none", "")
			end
		end
	elseif scope == "station" then
		if menu.manager and GetNPCBlackboard(menu.manager, "$St_Prod_Data") then
			local station = GetComponentData(menu.module,"parent")
			if station then 
				menu.remotelog("Wiping the blackboard for " .. (GetComponentData(station,"name") or "Unknown Station"))
			else
				menu.remotelog("Wiping the blackboard for Unknown Station")
			end
			SetNPCBlackboard(menu.manager, "$St_Prod_Data", "") -- wipe the BlackBoard
			SetNPCBlackboard(menu.manager, "$St_Prod_DataKeys", "") -- wipe the keys
		end
	end
end

function menu.stringf(s)
	return string.format("%g", string.format("%1.2f", s))
end

function menu.remotelog(s)
	local version = tonumber(string.sub(GetVersionString(), string.find(GetVersionString(), '(',1,true) + 1, string.find(GetVersionString(), '(',1,true) + 6)) or 208022
	-- DebugError("remotelog " .. version .. " " .. tostring(s))
	if version < 208022 then -- compensate for AddUITriggeredEvent parameter change
		AddUITriggeredEvent("St_Prod_Logger", s)
	else
		AddUITriggeredEvent("St_Prod_Logger", "", s)
	end
end

function menu.onUpdate()
end

function menu.onRowChanged(row, rowdata)
	if rowdata then
		
	end
end

function menu.onSelectElement()
	local rowdata = Helper.currentTableRowData
	if rowdata then
	end
end

function menu.onCloseElement(dueToClose)
	if dueToClose == "close" then
		Helper.closeMenuAndCancel(menu)
		menu.cleanup()
	else
		Helper.closeMenuAndReturn(menu)
		menu.cleanup()
	end
end

init()
local menu = {
	name = "Station_Prod_Limit",
}

local function init()
	Menus = Menus or { }
	table.insert(Menus, menu)
	if Helper then
		Helper.registerMenu(menu)
	end
end

function menu.cleanup()
	menu.title = nil
	menu.method = nil
	menu.module = nil
	menu.production = nil

	menu.infotable = nil
	menu.selecttable = nil
	menu.prodbrake = nil
	menu.maxslider = nil
	menu.moduid = nil
	menu.manager = nil
	menu.priware = nil
	menu.product = nil
	menu.slider1val = nil
	menu.cargo = nil
end

-- Menu member functions

-- Button Scripts
function menu.buttonOK()	
	if GetSliderValue(menu.slider) then
		local value1, value2, value3, value4 = GetSliderValue(menu.slider)  -- get slider value
		local prodbrake = tonumber(value1 / menu.maxslider) or 0 -- production brake or recycle factor
		local mdproddata
		if menu.prodbrake ~= prodbrake then -- its changed
			if prodbrake ~= 0 then
				if menu.manager then
					mdproddata = GetNPCBlackboard(menu.manager, "$St_Prod_Data") or {}
					if mdproddata == "" then -- force mdproddata to be a table
						mdproddata = {}
					end
					if mdproddata[menu.moduid] then -- set prodbrake on the BlackBoard
						mdproddata[menu.moduid][4] = prodbrake
						SetNPCBlackboard(menu.manager, "$St_Prod_Data", mdproddata) -- update the BlackBoard
					else -- create new BlackBoard entry
						local sequence, stage = GetComponentData(menu.module, "sequence", "stage")
						if sequence and sequence == "" then 
							sequence = "a" 
						end
						local products = {}
						if menu.production and menu.production.products and #menu.production.products > 0 then -- needs a Production method
							products[1] = {ware=menu.production.products[1].ware, name=menu.production.products[1].name}
							local exportproddata = {GetCurTime(), sequence, stage, tonumber(prodbrake), 0, "unknown", 0, 100, -10, 0, products, {}, {}}
							mdproddata[menu.moduid] = exportproddata
							SetNPCBlackboard(menu.manager, "$St_Prod_Data", mdproddata) -- update $St_Prod_Data on the BlackBoard
							local proddatakeys = {}
							for k,v in pairs(mdproddata) do
								table.insert(proddatakeys, k)
							end
							SetNPCBlackboard(menu.manager, "$St_Prod_DataKeys", proddatakeys) -- pass the keys to allow hash to be read in md script
						end
					end
				else
					menu.remotelog(ReadText(360003, 1) .. " requires a station production manager")
				end
			else
				if menu.manager then
					mdproddata = GetNPCBlackboard(menu.manager, "$St_Prod_Data") or {}		
					if mdproddata[menu.moduid] then -- remove the BlackBoard entry
						mdproddata[menu.moduid] = nil
						local numentries = 0
						for k,v in pairs(mdproddata) do
							numentries = numentries + 1
						end
						if numentries > 0 then
							SetNPCBlackboard(menu.manager, "$St_Prod_Data", mdproddata) -- update the BlackBoard
							local proddatakeys = {}
							for k,v in pairs(mdproddata) do
								table.insert(proddatakeys, k)
							end
							SetNPCBlackboard(menu.manager, "$St_Prod_DataKeys", proddatakeys) -- pass the keys to allow hash to be read in md script
						else
							menu.remotelog("Wiping the blackboard for " .. GetComponentData(GetComponentData(menu.module,"parent"),"name"))
							SetNPCBlackboard(menu.manager, "$St_Prod_Data", "") -- wipe the BlackBoard
							SetNPCBlackboard(menu.manager, "$St_Prod_DataKeys", "") -- wipe the keys
						end
					end
				end
			end
		end
	else
		menu.remotelog("Unable to get Slider value for " .. menu.warename .. " - Skipping")
	end
	Helper.closeMenuAndReturn(menu, nil)
	menu.cleanup()
end

function menu.buttonConfig()
	Helper.closeMenuForSubSection(menu, false, "gMain_Station_Prod_Config", { 0, 0, menu.priware, menu.module })
	menu.cleanup()
end

function menu.onShowMenu()
	menu.method = menu.param[3]
	menu.warename = menu.param[4]
	menu.module = menu.param[5]
	
	menu.production = GetLibraryEntry("productionmethods", menu.method) or nil
	local menuname = ReadText(1001, 2408)
	if menu.production and menu.production.products and #menu.production.products > 0 then
		menu.product = menu.production.products[1].name
		menu.priware = menu.production.products[1].ware
	else
		menu.remotelog(menu.warename .. ". No Production method from GetLibrary('productionmethods').")
		menu.product = menu.warename
		menu.priware = ReadText(20212, 101)
	end
	menu.title = ReadText(360003, 1) .. " - " .. menu.product

	-- get build stage and sequence to use as unique key for this prod module
	local sequence, stage = GetComponentData(menu.module, "sequence", "stage")
	if sequence and sequence == "" then 
		sequence = "a" 
	end
	menu.moduid = sequence .. "_" .. stage .. "_" .. menu.priware
	menu.prodbrake = 0
	local station = GetComponentData(menu.module,"parent")
	if station then
		menu.manager = GetComponentData(station, "tradenpc")
		if menu.manager then
			local mdproddata = GetNPCBlackboard(menu.manager, "$St_Prod_Data") or {}		
			if mdproddata[menu.moduid] then -- get prodbrake from the BlackBoard
				menu.prodbrake = mdproddata[menu.moduid][4]
			end
		end
	end

	local setup = Helper.createTableSetup(menu)
	setup:addSimpleRow({
		Helper.createButton(nil, Helper.createButtonIcon("dock_repair_active", nil, 255, 255, 255, 100), false),
		Helper.createFontString(menu.title, false, "left", 255, 255, 255, 100, Helper.headerRow1Font, Helper.headerRow1FontSize, false, Helper.headerRow1Offsetx, Helper.headerRow1Offsety, Helper.headerRow1Height, Helper.headerRow1Width)
	}, nil, {1, 1}, false, Helper.defaultTitleBackgroundColor)
	setup:addTitleRow({ 
		Helper.createFontString("", false, "left", 129, 160, 182, 100, Helper.headerRow2Font, Helper.headerRow2FontSize, false, Helper.headerRow2Offsetx, Helper.headerRow2Offsety, Helper.headerRow2Height, Helper.headerRow1Width)	-- text depends on selection
	}, nil, {2})
	local infodesc = setup:createCustomWidthTable({Helper.headerRow1Height, 0}, false, false, true, 2, 1)

	setup = Helper.createTableSetup(menu)
	setup:addSimpleRow({
		ReadText(1001, 1603), 
		ConvertTimeString(menu.production.cycle or 0, "%h" .. ReadText(1001, 102) .. " %M" .. ReadText(1001, 103) .. " %S" .. ReadText(1001, 100))
	}, nil, {3, 1})

	-- Products
	menu.addWares(setup, menu.production.products, ReadText(1001, 1610), 1)
	-- Primary resources
	menu.addWares(setup, menu.production.presources, ReadText(1001, 1611), -1)
	-- Secondary resources
	-- no space :(
	
	setup:addFillRows(14, nil, {4})

	local resources = {} -- find the smallest amount from presources, sresources and products
	menu.maxslider = 100
	if menu.production then
		if menu.production.presources then
			table.insert(resources, menu.production.presources)
		end
		if menu.production.sresources then
			table.insert(resources, menu.production.sresources)
		end
		if menu.production.products then
			table.insert(resources, menu.production.products)
		end
		
		for _, slidersource in ipairs(resources) do
			if #slidersource ~= 0 then -- find smallest ware amount		
				for _, resource in ipairs(slidersource) do
					if resource.amount < menu.maxslider then
						menu.maxslider = resource.amount
					end
				end
			end
		end
	end

	local sliderinfo = {
		["background"] = "tradesellbuy_blur", 
		["captionLeft"] = "", 
		["captionCenter"] = ReadText(360003, 100), 
		["captionRight"] = "", 
		["min"] = 0,
		["minSelectable"] = 0,
		["max"] = menu.maxslider,
		["zero"] = 0,
		["start"] = math.floor(menu.prodbrake * menu.maxslider) or 0
	}
	local scale1info = { 
		["left"] = nil,
		["right"] = nil,
		["center"] = false,
		["inverted"] = false,
		["suffix"] = nil
	}
	local scale2info = {
		["left"] = nil,
		["right"] = nil,
		["center"] = true,
		["factor"] = (100 / menu.maxslider),
		["inverted"] = false,
		["suffix"] = "%"
	}
	local sliderdesc = Helper.createSlider(sliderinfo, scale1info, scale2info, 3, Helper.sliderOffsetx, 443)
	setup:addSimpleRow({ 
		Helper.createButton(Helper.createButtonText(ReadText(1001, 2669), "center", Helper.standardFont, Helper.standardFontSize, 255, 255, 255, 100), nil, false, true, 2 * Helper.standardTextOffsetx, 0.8 * Helper.standardTextHeight, 150, 25, nil, Helper.createButtonHotkey("INPUT_STATE_DETAILMONITOR_B", true)),
		Helper.getEmptyCellDescriptor(),
		Helper.getEmptyCellDescriptor(),
		Helper.createButton(Helper.createButtonText(ReadText(1001, 14), "center", Helper.standardFont, Helper.standardFontSize, 255, 255, 255, 100), nil, false, true, 7 * Helper.standardTextOffsetx, 0.8 * Helper.standardTextHeight, 150, 25, nil, Helper.createButtonHotkey("INPUT_STATE_DETAILMONITOR_A", true)),
	}, nil, {1, 1, 1, 1, 1}, false, menu.transparent)
	local selectdesc = setup:createCustomWidthTable({0, 200, 200, 200}, false, false, true, 1, 0, 0, Helper.tableOffsety)

	-- create tableview
	menu.infotable, menu.selecttable, menu.slider = Helper.displayTwoTableSliderView(menu, infodesc, selectdesc, sliderdesc, true)

	-- set button scripts
	Helper.setButtonScript(menu, nil, menu.infotable, 1, 1, menu.buttonConfig)
	Helper.setButtonScript(menu, nil, menu.selecttable, 15, 1, function () return menu.onCloseElement("back") end)
	Helper.setButtonScript(menu, nil, menu.selecttable, 15, 4, menu.buttonOK)

	-- clear descriptors again
	Helper.releaseDescriptors()
end

 -- menu.updateInterval = 2.0

function menu.addWares(setup, waretable, title, usage)
	local prodrate = tonumber(1 - menu.prodbrake) or 1 -- production rate
	local container = GetContextByClass(menu.module, "container", false)
	local cargo = {}
	if container then 
		cargo = GetComponentData(container, "cargo") or {}
	end
	
	setup:addHeaderRow({ 
		title, 
		(usage > 0 and ReadText(1001, 1600) or ReadText(1001, 1609)) .. " (" .. ReadText(360003, 10) .. ")",
		(usage > 0 and ReadText(1001, 1600) or ReadText(1001, 1609)) .. " (" .. ReadText(1001, 102) .. ")",
		ReadText(1001, 20) .. " / " .. ReadText(1001, 1127) 
		})
	if waretable then
		if #waretable == 0 then
			setup:addSimpleRow({ 
				Helper.getEmptyCellDescriptor(), 
				"-- " .. ReadText(1001, 32) .. " --", 
				Helper.getEmptyCellDescriptor() 
			})
		else
			for _, resource in ipairs(waretable) do
				setup:addSimpleRow({
					resource.name, 
					Helper.createFontString(ConvertIntegerString(math.floor(resource.amount * prodrate + 0.5),true, 4, true)  .. " / " .. ConvertIntegerString(math.floor(resource.amount + 0.5),true, 4, true) , false, "right"),
					Helper.createFontString(ConvertIntegerString(resource.amount * prodrate * 3600 / menu.production.cycle, true, 4, true) .. " / " .. ConvertIntegerString(resource.amount * 3600 / menu.production.cycle, true, 4, true), false, "right"),
					Helper.createFontString(ConvertIntegerString(cargo[resource.ware] or 0, true, 4, true) .. " / " .. ConvertIntegerString(GetWareProductionLimit(container, resource.ware) or 0, true, 4, true), false, "right") 
				}, resource.ware)
			end
		end
	end
end

function menu.remotelog(s)
	local version = tonumber(string.sub(GetVersionString(), string.find(GetVersionString(), '(',1,true) + 1, string.find(GetVersionString(), '(',1,true) + 6)) or 208022
	-- DebugError("remotelog " .. version .. " " .. tostring(s))
	if version < 208022 then -- compensate for AddUITriggeredEvent parameter change
		AddUITriggeredEvent("St_Prod_Logger", s)
	else
		AddUITriggeredEvent("St_Prod_Logger", "", s)
	end
end

function menu.onUpdate()	
	local container = GetContextByClass(menu.module, "container", false)
	local cargo = {}
	if container then 
		local cargo = GetComponentData(container, "cargo") or {}
	end
	if GetSliderValue(menu.slider) then -- make sure that the slider has data
		local value1, value2, value3, value4 = GetSliderValue(menu.slider)  -- get slider value
		local prodbrake = tonumber(value1 / menu.maxslider) or 0 -- production brake or recycle factor
		local prodrate = tonumber(1 - prodbrake) or 1 -- production rate
		local linenum = 3
		if menu.production and menu.production.products and #menu.production.products ~= 0 then -- update Products		
			for _, resource in ipairs(menu.production.products) do
				if menu.slider1val and menu.slider1val ~= value1 then -- update on change only
					-- menu.remotelog("value1: " .. tostring(value1))
					Helper.updateCellText(menu.selecttable, linenum, 2, ConvertIntegerString(math.floor(resource.amount * prodrate + 0.5),true, 4, true)  .. " / " .. ConvertIntegerString(math.floor(resource.amount + 0.5),true, 4, true) )
					Helper.updateCellText(menu.selecttable, linenum, 3, ConvertIntegerString(resource.amount * prodrate * 3600 / menu.production.cycle, true, 4, true) .. " / " .. ConvertIntegerString(resource.amount * 3600 / menu.production.cycle, true, 4, true))
				end
				if menu.cargo and cargo and menu.cargo[resource.ware] ~= cargo[resource.ware] then -- update on change only
					Helper.updateCellText(menu.selecttable, linenum, 4, ConvertIntegerString(cargo[resource.ware] or 0, true, 4, true) .. " / " .. ConvertIntegerString(GetWareProductionLimit(container, resource.ware) or 0, true, 4, true))
				end
				linenum = linenum + 1
			end
		end
		linenum = linenum + 1 -- skip header row
		if menu.production and menu.production.presources and #menu.production.presources ~= 0 then -- update Primary Resources		
			for _, resource in ipairs(menu.production.presources) do
				if menu.slider1val and menu.slider1val ~= value1 then -- update on change only
					Helper.updateCellText(menu.selecttable, linenum, 2, ConvertIntegerString(math.floor(resource.amount * prodrate + 0.5),true, 4, true)  .. " / " .. ConvertIntegerString(math.floor(resource.amount + 0.5),true, 4, true))
					Helper.updateCellText(menu.selecttable, linenum, 3, ConvertIntegerString(resource.amount * prodrate * 3600 / menu.production.cycle, true, 4, true) .. " / " .. ConvertIntegerString(resource.amount * 3600 / menu.production.cycle, true, 4, true))
				end
				if menu.cargo and cargo and menu.cargo[resource.ware] ~= cargo[resource.ware] then -- update on change only
					Helper.updateCellText(menu.selecttable, linenum, 4, ConvertIntegerString(cargo[resource.ware] or 0, true, 4, true) .. " / " .. ConvertIntegerString(GetWareProductionLimit(container, resource.ware) or 0, true, 4, true))
				end
				linenum = linenum + 1
			end
		end
		menu.slider1val = value1
		menu.cargo = cargo
	end
end

function menu.onRowChanged(row, rowdata)
end

function menu.onSelectElement()
end

function menu.onCloseElement(dueToClose)
	if dueToClose == "close" then
		Helper.closeMenuAndCancel(menu)
		menu.cleanup()
	else
		Helper.closeMenuAndReturn(menu)
		menu.cleanup()
	end
end

init()

-- section == gMain_objectProduction
-- param == { 0, 0, object, productionmodule }

local menu = {
	name = "Station_Production",
	white = { r = 255, g = 255, b = 255, a = 100 },
	orange = { r = 255, g = 192, b = 0, a = 100 },
	red = { r = 255, g = 0, b = 0, a = 100 }
}

local function init()
	Menus = Menus or { }
	table.insert(Menus, menu)
	if Helper then
		Helper.registerMenu(menu)
	end
	RegisterEvent("St_Prod_RemainingCycletime", menu.St_Prod_RemainingCycletime)
	RegisterEvent("St_Prod_Player", menu.St_Prod_Player)
end

function menu.cleanup()
	menu.title = nil
	menu.object = nil
	menu.container = nil
	menu.module = nil
	menu.category = nil
	menu.proddata = {}
	menu.unlocked = {}
	menu.zoneowner = nil
	menu.owner = nil

	menu.infotable = nil
	menu.selecttable = nil
end

-- Menu member functions

function menu.buttonShipEncyclopedia()
	Helper.closeMenuForSubSection(menu, false, "gEncyclopedia_object", { 0, 0, menu.category, GetComponentData(menu.object, "macro"), menu.category == "stationtypes" })
	menu.cleanup()
end

function menu.buttonEncyclopedia(type, id)
	local version = tonumber(string.sub(GetVersionString(), string.find(GetVersionString(), '(',1,true) + 1, string.find(GetVersionString(), '(',1,true) + 6)) or 208022
	if type == "ware" then
		if version < 208022 then -- compensate for gEncyclopedia_ware parameter change
			Helper.closeMenuForSubSection(menu, false, "gEncyclopedia_ware", { 0, 0, id })
		else
			Helper.closeMenuForSubSection(menu, false, "gEncyclopedia_ware", { 0, 0, "wares", id })
		end
		menu.cleanup()
	elseif type == "moduletypes_efficiency" or type == "moduletypes_production" then
		Helper.closeMenuForSubSection(menu, false, "gEncyclopedia_object", { 0, 0, type, id, false })
		menu.cleanup()
	end
end

function menu.buttonConfigBrake(type, ware, prodmethod)
	if type == "ware" then
		Helper.closeMenuForSubSection(menu, false, "gMain_Station_Prod_Slider", { 0, 0, prodmethod, ware, menu.module })
		menu.cleanup()
	end
end 

function menu.onShowMenu()
	menu.object = menu.param[3]
	menu.module = menu.param[4]
	menu.unlocked = {}

	-- if player off ship then can't get player object
	local version = tonumber(string.sub(GetVersionString(), string.find(GetVersionString(), '(',1,true) + 1, string.find(GetVersionString(), '(',1,true) + 6)) or 208022
	local playership = GetPlayerPrimaryShipID()
    if playership then 
		local player = GetComponentData(playership, "controlentity")
		if not player then -- ask md script to send player to menu.St_Prod_Player which sets menu.player
			if version < 208022 then -- compensate for AddUITriggeredEvent parameter change
				AddUITriggeredEvent("St_Prod_GetPlayer", "")
			else
				AddUITriggeredEvent("St_Prod_GetPlayer", "none", "")
			end
		end
	else
		if version < 208022 then -- compensate for AddUITriggeredEvent parameter change
			AddUITriggeredEvent("St_Prod_GetPlayer", "")
		else
			AddUITriggeredEvent("St_Prod_GetPlayer", "none", "")
		end
	end

	menu.container = GetContextByClass(menu.object, "container", false)
	local name = GetComponentData(menu.object, "name")
	if menu.container then
		menu.title = GetComponentData(menu.container, "name") .. " - " .. (name ~= "" and name or ReadText(1001, 56))
	else
		menu.title = (name ~= "" and name or ReadText(1001, 56)) .. " - " .. GetComponentData(menu.module, "name")
	end
		
	menu.owner = GetComponentData(menu.object, "owner")
	local zone = GetComponentData(menu.object, "zoneid")
	menu.zoneowner = GetComponentData(zone, "owner")
	local zoneownername = GetComponentData(zone, "ownername")

	if IsComponentClass(menu.object, "station") then
		menu.category = "stationtypes"
	elseif IsComponentClass(menu.object, "ship_xl") then
		menu.category = "shiptypes_xl"
	elseif IsComponentClass(menu.object, "ship_l") then
		menu.category = "shiptypes_l"
	elseif IsComponentClass(menu.object, "ship_m") then
		menu.category = "shiptypes_m"
	elseif IsComponentClass(menu.object, "ship_s") then
		menu.category = "shiptypes_s"
	elseif IsComponentClass(menu.object, "ship_xs") then
		menu.category = "shiptypes_xs"
	else
		menu.category = GetModuleType(menu.object)
	end

	local setup = Helper.createTableSetup(menu)
	setup:addSimpleRow({
		Helper.createButton(nil, Helper.createButtonIcon("menu_info", nil, 255, 255, 255, 100), false),
		Helper.createFontString(menu.title .. (menu.owner == "player" and "" or " (" .. GetComponentData(menu.object, "revealpercent") .. " %)"), false, "left", 255, 255, 255, 100, Helper.headerRow1Font, Helper.headerRow1FontSize, false, Helper.headerRow1Offsetx, Helper.headerRow1Offsety, Helper.headerRow1Height, Helper.headerRow1Width)
	}, nil, {1, 1}, false, Helper.defaultTitleBackgroundColor)
	setup:addTitleRow({ 
		Helper.createFontString(menu.zoneowner and string.format(ReadText(1001, 72), zoneownername) or "", false, "left", 129, 160, 182, 100, Helper.headerRow2Font, Helper.headerRow2FontSize, false, Helper.headerRow2Offsetx, Helper.headerRow2Offsety, Helper.headerRow2Height, Helper.headerRow1Width)	-- text depends on selection
	}, nil, {2})
	local infodesc = setup:createCustomWidthTable({Helper.headerRow1Height, 0}, false, false, true, 2, 1)

	setup = Helper.createTableSetup(menu)
	--[[ GetProductionModuleData() return value = {
		state = "ProductionState",
		cycletime = CycleTime (only if state == "producing", otherwise 0),
		cycleefficiency = cycle efficiency percentage (100% default),
		remainingcycletime = Time remaining for this cycle (only if state == "producing", otherwise 0),
		cycleprogress = percentage of current cycle progress (only if state = "producing", otherwise 0),
		remainingtime = time until out of resources (does not take limited storage space into account),
		products = {
			efficiency = product efficiency percentage (100% default),
			[1] = { ware = "wareid", name = "Ware Name", amount = storageamount, cycle = cycleamount },
			[2] = ...
		},
		presources = { ... },    -- primary resources, analogous to products table
		sresources = { ... },    -- secondary resources, analogous to products table
	} ]]
	menu.proddata = GetProductionModuleData(menu.module)
	menu.unlocked.production_products = IsInfoUnlockedForPlayer(menu.module, "production_products")
	menu.unlocked.production_resources = IsInfoUnlockedForPlayer(menu.module, "production_resources")
	menu.unlocked.production_rate = IsInfoUnlockedForPlayer(menu.module, "production_rate")
	menu.unlocked.consumption_rate = IsInfoUnlockedForPlayer(menu.module, "consumption_rate")
	menu.unlocked.production_time = IsInfoUnlockedForPlayer(menu.module, "production_time")
	menu.unlocked.efficiency_type = IsInfoUnlockedForPlayer(menu.module, "efficiency_type")
	menu.unlocked.efficiency_amount = IsInfoUnlockedForPlayer(menu.module, "efficiency_amount")
	
	if menu.proddata.productionmethod then
		AddKnownItem("productionmethods", menu.proddata.productionmethod)
	end
	
	if menu.proddata.state == "empty" then
		setup:addSimpleRow({ ReadText(1001, 1601) })
	else
		setup:addHeaderRow({ 
			ReadText(1001, 1600), 
			ReadText(1001, 24)
		}, nil, {3, 1})
		if menu.proddata.state == "noresources" then
			setup:addSimpleRow({ 
				Helper.unlockInfo(menu.unlocked.production_time, Helper.createFontString(ReadText(1001, 1604), false, "left", 255, 0, 0, 100)) 
			}, nil, {3, 1})
		elseif menu.proddata.state == "nostorage" then
			setup:addSimpleRow({ 
				Helper.unlockInfo(menu.unlocked.production_time, Helper.createFontString(ReadText(1001, 1605), false, "left", 255, 0, 0, 100)) 
			}, nil, {3, 1})
		elseif menu.proddata.state ~= "producing" then
			setup:addSimpleRow({ 
				Helper.unlockInfo(menu.unlocked.production_time, ReadText(1001, 1606)) 
			}, nil, {4})
		else
			setup:addSimpleRow({ 
				string.format("%s (%s %%)", ReadText(1001, 1607), Helper.unlockInfo(menu.unlocked.production_time, math.floor(menu.proddata.cycleprogress))), 
				Helper.createFontString(Helper.unlockInfo(menu.unlocked.production_time, ConvertTimeString(menu.proddata.remainingcycletime, "%h" .. ReadText(1001, 102) .. " %M" .. ReadText(1001, 103) .. " %S" .. ReadText(1001, 100))), false, "right") 
			}, nil, {3, 1})
		end
		if menu.proddata.state == "producing" and menu.proddata.remainingtime == 0 then
			local timestring = "4.2 " .. ReadText(1001, 112)
			local clustermacro = GetComponentData(GetComponentData(menu.object, "clusterid"), "macro")
			if clustermacro == "cluster_a_macro" then
				-- Maelstrom
				timestring = "0.9 " .. ReadText(1001, 112)
			elseif clustermacro == "cluster_b_macro" then
				-- Albion
				timestring = "4.2 " .. ReadText(1001, 112)
			elseif clustermacro == "cluster_c_macro" then
				-- Omycron Lyrae
				timestring = "3.7 " .. ReadText(1001, 112)
			elseif clustermacro == "cluster_d_macro" then
				-- DeVries
				timestring = "11.4 " .. ReadText(1001, 112)
			end
			setup:addSimpleRow({ 
				ReadText(1001, 1608), 
				Helper.createFontString(Helper.unlockInfo(menu.unlocked.production_time, timestring), false, "right") 
			}, nil, {3, 1})
		else
			setup:addSimpleRow({ 
				ReadText(1001, 1608), 
				Helper.createFontString(Helper.unlockInfo(menu.unlocked.production_time, ConvertTimeString(menu.proddata.remainingtime , "%h" .. ReadText(1001, 102) .. " %M" .. ReadText(1001, 103) .. " %S" .. ReadText(1001, 100))), false, "right") 
			}, nil, {3, 1})
		end

		menu.addWares(setup, menu.proddata.products, ReadText(1001, 1610), 1)
		menu.addWares(setup, menu.proddata.presources, ReadText(1001, 1611), -1)
		menu.addWares(setup, menu.proddata.sresources, ReadText(1001, 1612), -1)

		if menu.owner == "player" then 
			menu.addProdLimit(setup, menu.module)
		end
		
		local name, hullpercent, threshold, efffactor, effbonus = GetComponentData(menu.module, "name", "hullpercent", "efficiencythreshold", "efficiencyfactor", "efficiencybonus")
		local infobutton = Helper.createButton(nil, Helper.createButtonIcon("menu_info", nil, 255, 255, 255, 100), false, menu.unlocked.efficiency_type)
		menu.proddata.efficiencyupgrades = GetEfficiencyUpgrades(menu.module)

		menu.addEfficiencyEntries(setup, "product", name, hullpercent, threshold, efffactor, effbonus)
		--menu.addEfficiencyEntries(setup, "primary", name, hullpercent, threshold, efffactor, effbonus)
		--menu.addEfficiencyEntries(setup, "cycle", name, hullpercent, threshold, efffactor, effbonus)
	end

	setup:addFillRows(17)

	local selectdesc = setup:createCustomWidthTable({ Helper.standardButtonWidth, 0, 176, 176 }, false, false, true, 1, 0, 0, Helper.tableOffsety)
	-- create tableview
	menu.infotable, menu.selecttable = Helper.displayTwoTableView(menu, infodesc, selectdesc, false)

	-- set button scripts
	Helper.setButtonScript(menu, nil, menu.infotable, 1, 1, menu.buttonShipEncyclopedia)
	local nooflines = 4
	if menu.proddata.state ~= "empty" then
		nooflines = menu.addWareButtons(menu.proddata.products, nooflines)
		nooflines = menu.addWareButtons(menu.proddata.presources, nooflines)
		nooflines = menu.addWareButtons(menu.proddata.sresources, nooflines)
		if menu.owner == "player" then 
			nooflines = menu.addProdLimitButtons(GetPossibleProducts(menu.module), nooflines)
		end
	end

	-- clear descriptors again
	Helper.releaseDescriptors()
end

menu.updateInterval = 0.2

function menu.St_Prod_UpdateModuleInfo(module, prodbrake)
	local station = GetComponentData(module,"parent")
	local manager
	if station then 
		manager = GetComponentData(station, "tradenpc")
	end

	if manager then -- must have a manager or nothing happens
		local proddata = GetProductionModuleData(module)
		
		-- get the global debug level from player BlackBoard
		local playership = GetPlayerPrimaryShipID()
		if playership then 
			local player = GetComponentData(playership, "controlentity")
			-- menu.remotelog("Playership is " .. tostring(playership) .. " player is " .. tostring(player))
			if player then 
				menu.player = player
			end
		end

		local globaldebug = 0
		if menu.player then 
			globaldebug = GetNPCBlackboard(menu.player, "$St_Prod_Data_Debug") or 0		
		end

		-- get the station debug level from manager BlackBoard
		local stationdebug = GetNPCBlackboard(manager, "$St_Prod_Data_Debug")		
		if not stationdebug or stationdebug == "" then
			stationdebug = 0
		end
		-- menu.remotelog("production_menu_st_prod Global: " .. tostring(globaldebug) .. " Station: " .. tostring(stationdebug))

		local effectivedebug = tonumber(globaldebug) -- effectivedebug is the higher of the two
		if tonumber(stationdebug) > tonumber(globaldebug) then
			effectivedebug = tonumber(stationdebug)
		end
	
		-- get build stage and sequence to use as unique key for this prod module
		local sequence, stage = GetComponentData(module, "sequence", "stage")
		if sequence and sequence == "" then 
			sequence = "a" 
		end
		local buildstage = "[" .. sequence .. "," .. stage .. "]"
		local moduid = sequence .. "_" .. stage .. "_" .. proddata.products[1]["ware"]

		-- get production data for this manager
		local mdproddata = GetNPCBlackboard(manager, "$St_Prod_Data") or {}		
		local skipmodule = false
		local prevremainingcycletime
		
		if mdproddata[moduid] then -- get prodbrake and debug from the BlackBoard
			local mdprodbrake = mdproddata[moduid][4]
			local mdprevremainingcycletime = mdproddata[moduid][9] -- take the previous remainingcycletime
			local mddebug = mdproddata[moduid][5] or 0
			if not prodbrake then
				prodbrake = tonumber(mdprodbrake)
			end
			prevremainingcycletime = tonumber(mdprevremainingcycletime)
		else
			prevremainingcycletime = -10
			if not prodbrake then -- do not add to BlackBoard unless prodbrake set (turret, urv or missile forge)
				skipmodule = true
				if effectivedebug > 1 then 
					menu.remotelog(GetComponentData(station, "name") .. " - " .. GetComponentData(module, "name") .. ". Skipping " .. proddata.products[1]["ware"] .. "s. Remaining " .. string.format("%1.2f", proddata.remainingcycletime) .. "s  - not limited.")
				end
			end		
		end
		
		if prodbrake and prodbrake == 0 then -- stop braking this module
			skipmodule = true
			menu.remotelog("Skipping " .. GetComponentData(station, "name") .. " " .. moduid .. " prodbrake removed")
		end
			
		local proddatakeys = {}
		-- pass production data for this module onto the manager
		local exportproddata = {GetCurTime(), sequence, stage, tonumber(prodbrake), mddebug ,proddata.state, proddata.cycletime, proddata.cycleefficiency, tonumber(proddata.remainingcycletime), prevremainingcycletime, proddata.products, proddata.presources, proddata.sresources}
		mdproddata[moduid] = exportproddata
		if skipmodule == false then -- update BlackBoard
			if effectivedebug > 0 then
				local zonename = GetComponentData(station, "zone") or "UnknownZone"
				menu.remotelog("Polling: " .. GetComponentData(station, "name") .. " - " .. tostring(zonename) .. " " .. moduid .. " rem_cycletime: " .. string.format("%4.3f", proddata.remainingcycletime) .. "s" .. " prevrem_cycletime: " .. string.format("%4.3f", prevremainingcycletime) .. "s")
			end
			SetNPCBlackboard(manager, "$St_Prod_Data", mdproddata)
			for k,v in pairs(mdproddata) do
				table.insert(proddatakeys, k)
			end
			SetNPCBlackboard(manager, "$St_Prod_DataKeys", proddatakeys) -- pass the keys to allow hash to be read in md script
		end
	else
		menu.remotelog("There is no Production Manager on " .. GetComponentData(menu.object, "name") .. " Cannot limit production" )	
	end
end

function menu.St_Prod_RemainingCycletime(eventname, module)
	-- menu.remotelog("Event: " .. eventname .. " , " .. GetComponentData(module, "name"))
	menu.St_Prod_UpdateModuleInfo(module)
end

function menu.St_Prod_Player(eventname, player)
	-- menu.remotelog("Player from md: " .. eventname .. " , " .. tostring(player))
	menu.player = player
end

function menu.onUpdate()
	local proddata = GetProductionModuleData(menu.module)
	if next(proddata) then
		if proddata.state == "producing" then
			if math.floor(menu.proddata.cycleprogress) ~= math.floor(proddata.cycleprogress) then
				menu.proddata.cycleprogress = proddata.cycleprogress
				if menu.unlocked.production_time then
					Helper.updateCellText(menu.selecttable, 2, 1, string.format("%s (%s %%)", ReadText(1001, 1607), math.floor(proddata.cycleprogress)))
				end
			end
			if math.floor(menu.proddata.remainingcycletime) ~= math.floor(proddata.remainingcycletime) then
				menu.proddata.remainingcycletime = proddata.remainingcycletime
				if menu.unlocked.production_time then
					Helper.updateCellText(menu.selecttable, 2, 4, ConvertTimeString(proddata.remainingcycletime, "%h" .. ReadText(1001, 102) .. " %M" .. ReadText(1001, 103) .. " %S" .. ReadText(1001, 100)))
				end
			end
			if math.floor(menu.proddata.remainingtime) ~= math.floor(proddata.remainingtime) then
				menu.proddata.remainingtime = proddata.remainingtime
				if menu.unlocked.production_time then
					Helper.updateCellText(menu.selecttable, 3, 4, ConvertTimeString(proddata.remainingtime, "%h" .. ReadText(1001, 102) .. " %M" .. ReadText(1001, 103) .. " %S" .. ReadText(1001, 100)))
				end
			end
			menu.updateWares(proddata.products, menu.proddata.products, 4, 1)
			menu.updateWares(proddata.presources, menu.proddata.presources, 5 + (#proddata.products > 0 and #proddata.products or 1), -1)
			menu.updateWares(proddata.sresources, menu.proddata.sresources, 6 + (#proddata.products > 0 and #proddata.products or 1) + (#proddata.presources > 0 and #proddata.presources or 1), -1)
		else
			if proddata.state == "noresources" then
				Helper.updateCellText(menu.selecttable, 2, 1, Helper.unlockInfo(menu.unlocked.production_time, Helper.createFontString(ReadText(1001, 1604), false, "left", 255, 0, 0, 100)))
			elseif proddata.state == "nostorage" then
				Helper.updateCellText(menu.selecttable, 2, 1, Helper.unlockInfo(menu.unlocked.production_time, Helper.createFontString(ReadText(1001, 1605), false, "left", 255, 0, 0, 100)))
			elseif proddata.state ~= "producing" then
				Helper.updateCellText(menu.selecttable, 2, 1, Helper.unlockInfo(menu.unlocked.production_time, ReadText(1001, 1606)))
			end
			Helper.updateCellText(menu.selecttable, 3, 1, ReadText(1001, 1608))
			Helper.updateCellText(menu.selecttable, 3, 4, Helper.unlockInfo(menu.unlocked.production_time, ConvertTimeString(proddata.remainingtime , "%h" .. ReadText(1001, 102) .. " %M" .. ReadText(1001, 103) .. " %S" .. ReadText(1001, 100))))
		end
	end
end

function menu.onRowChanged(row, rowdata)
end

function menu.onSelectElement()
end

function menu.onCloseElement(dueToClose)
	if menu.interactive then
		if dueToClose == "close" then
			Helper.closeMenuAndCancel(menu)
			menu.cleanup()
		else
			Helper.closeMenuAndReturn(menu)
			menu.cleanup()
		end
	else
		if dueToClose == "close" then
			Helper.closeNonInteractiveMenuAndCancel(menu)
			menu.cleanup()
		else
			Helper.closeNonInteractiveMenuAndReturn(menu)
			menu.cleanup()
		end
	end
end

function menu.getEfficiencyString(eff, inverted)
	if eff == 1.0 then
		return ReadText(1001, 18)
	end
	if inverted then
		if eff == 0.0 then		-- infinity? o_O
			return "+ oo"
		end
		eff = 1.0 / eff
	end
	local sign = eff < 1.0 and "-" or "+"
	return string.format("%s %d %%", sign, math.abs(eff - 1.0) * 100 + 0.5)
end

function menu.addProdLimit(setup, module)
	local station = GetComponentData(module,"parent") or nil
	-- get build stage and sequence to use as unique key for this prod module
	local sequence, stage = GetComponentData(module, "sequence", "stage")
	if sequence and sequence == "" then 
		sequence = "a" 
	end
	local manager = GetComponentData(station, "tradenpc")
	local mdproddata = GetNPCBlackboard(manager, "$St_Prod_Data")
	
	setup:addHeaderRow({ 
		Helper.getEmptyCellDescriptor(), 
		ReadText(360003, 1),
		ReadText(1001, 13)
	}, nil, {1, 2, 1})
--	if module and IsComponentClass(module, "production") then
--		AddKnownItem("moduletypes_production", GetComponentData(module, "macro"))
--	end
	local productionInfoTable = GetPossibleProducts(module)
	if (productionInfoTable and #productionInfoTable == 0) or not manager then
		if not manager then
			setup:addSimpleRow({ 
				Helper.getEmptyCellDescriptor(), 
				"\27R -- " .. ReadText(20208, 601) .. " " .. ReadText(1001, 3605) .. " -- \27X" 
			})
		else
			setup:addSimpleRow({ 
				Helper.getEmptyCellDescriptor(), 
				"-- " .. ReadText(1001, 32) .. " --" 
			})
		end
	else
		if productionInfoTable then 
			for _, ware in ipairs(productionInfoTable) do
				local prodmethod = GetWareData(ware.ware, "productionmethod")
				local fixedprodmethod = menu.getprodmethod(menu.module, prodmethod) -- correct prod method
				menu.remotelog(_ .. " " .. ware.ware .. " - " .. fixedprodmethod)
				AddKnownItem("productionmethods", fixedprodmethod) -- make sure that you know about this production method

				local color = menu.white
				if menu.zoneowner and IsWareIllegalTo(ware.ware, menu.owner, menu.zoneowner) then
					color = menu.orange
				end
		
				local moduid = sequence .. "_" .. stage .. "_" .. ware.ware
				local mdprodbrake
				if mdproddata and mdproddata[moduid] then -- get prodbrake from the BlackBoard
					mdprodbrake = menu.stringf(mdproddata[moduid][4] * 100 * -1)
					-- menu.remotelog("addProdLimit " .. GetComponentData(station,"name") .. " - " .. GetComponentData(module,"name") .. " prodbrake: " .. mdprodbrake .. "%") 
				end

				setup:addSimpleRow({ 
					Helper.createButton(nil, Helper.createButtonIcon("dock_repair_active", nil, 255, 255, 255, 100), false, menu.unlocked.production_resources), 
					Helper.unlockInfo(menu.unlocked.production_resources, Helper.createFontString(ware.name, false, "left", color.r, color.g, color.b, color.a)), 
					Helper.createFontString((mdprodbrake or 0) .. " %", false, "right")
				}, nil, {1, 2, 1})
				AddKnownItem("wares", ware.ware)
			end
		end
	end
end

function menu.getprodmethod(module, prodmethod) -- fudge the production method
	local macro = GetComponentData(module, "macro")
	local prod = "default"
	if macro then 
		if string.match(macro, "ol_macro$" ) then
			prod = "omicron"
		elseif string.match(macro, "dv_macro$" ) then
			prod = "devries"
		elseif string.match(macro, "xe_macro$" ) then
			prod = "xenon"
		end
	end
	--menu.remotelog("Prod module - " .. prod)

	local newprodmethod = string.gsub(prodmethod, "default", tostring(prod))
	local libentry = GetLibraryEntry("productionmethods", newprodmethod) or nil
	if libentry then -- use the race specific production method instead of default
		return newprodmethod
	else
		return prodmethod
	end
end

function menu.stringf(s)
	return string.format("%g", string.format("%1.2f", s))
end

function menu.remotelog(s)
	local version = tonumber(string.sub(GetVersionString(), string.find(GetVersionString(), '(',1,true) + 1, string.find(GetVersionString(), '(',1,true) + 6)) or 208022
	-- DebugError("remotelog " .. version .. " " .. tostring(s))
	if version < 208022 then -- compensate for AddUITriggeredEvent parameter change
		AddUITriggeredEvent("St_Prod_Logger", s)
	else
		AddUITriggeredEvent("St_Prod_Logger", "", s)
	end
end

function menu.addProdLimitButtons(productionInfoTable, nooflines)
	nooflines = nooflines + 1
	if #productionInfoTable == 0 then
		nooflines = nooflines + 1
	else
		for _, ware in ipairs(productionInfoTable) do
			local prodmethod = GetWareData(ware.ware, "productionmethod")
			local fixedprodmethod = menu.getprodmethod(menu.module, prodmethod) -- correct prod method

			Helper.setButtonScript(menu, nil, menu.selecttable, nooflines, 1, function () return menu.buttonConfigBrake("ware", ware.name, fixedprodmethod) end)
			nooflines = nooflines + 1
		end
	end

	return nooflines
end

function menu.addWares(setup, waretable, title, usage)
	setup:addHeaderRow({ 
		Helper.createIcon("menu_info", false, 255, 255, 255, 100, 0, 0, Helper.standardTextHeight, Helper.standardButtonWidth), 
		title, 
		ReadText(1001, 20) .. " / " .. ReadText(1001, 1127), 
		(usage > 0 and ReadText(1001, 1600) or ReadText(1001, 1609)) .. " / " .. ReadText(1001, 102)
	})
	if #waretable == 0 then
		setup:addSimpleRow({ 
			Helper.getEmptyCellDescriptor(), 
			"-- " .. ReadText(1001, 32) .. " --" 
		})
	else
		for _, ware in ipairs(waretable) do
			local color = menu.white
			if menu.zoneowner and IsWareIllegalTo(ware.ware, menu.owner, menu.zoneowner) then
				color = menu.orange
			end
			setup:addSimpleRow({ 
				Helper.createButton(nil, Helper.createButtonIcon(GetWareData(ware.ware, "icon"), nil, 255, 255, 255, 100), false, usage > 0 and menu.unlocked.production_products or menu.unlocked.production_resources), 
				Helper.unlockInfo(usage > 0 and menu.unlocked.production_products or menu.unlocked.production_resources, Helper.createFontString(ware.name, false, "left", color.r, color.g, color.b, color.a)), 
				Helper.createFontString(Helper.estimateString(menu.proddata.estimated) .. ConvertIntegerString(ware.amount, true, 4, true) .. " / " .. ConvertIntegerString(GetWareProductionLimit(menu.container or menu.object, ware.ware), true, 4, true), false, "right"), 
				Helper.createFontString(Helper.unlockInfo(usage > 0 and menu.unlocked.production_rate or menu.unlocked.consumption_rate, ConvertIntegerString(usage * ware.cycle * 3600 / menu.proddata.cycletime, true, 4, true)), false, "right")
			})
			AddKnownItem("wares", ware.ware)
		end
	end
end

function menu.addWareButtons(waretable, nooflines)
	nooflines = nooflines + 1
	if #waretable == 0 then
		nooflines = nooflines + 1
	else
		for _, ware in ipairs(waretable) do
			Helper.setButtonScript(menu, nil, menu.selecttable, nooflines, 1, function () return menu.buttonEncyclopedia("ware", ware.ware) end)
			nooflines = nooflines + 1
		end
	end

	return nooflines
end

function menu.addEfficiencyEntries(setup, type, name, hullpercent, threshold, efffactor, effbonus)
	local sectiontitle = ""
	if type == "product" then
		sectiontitle = ReadText(1001, 1619)
	elseif type == "primary" then
		sectiontitle = ReadText(1001, 1620)
	elseif type == "cycle" then
		sectiontitle = ReadText(1001, 1621)
	else
		sectiontitle = ReadText(1001, 10000)
	end

	setup:addHeaderRow({ 
		sectiontitle,
		ReadText(1001, 13)
	}, nil, {3, 1})
		
	local displayed = false

	if menu.proddata.efficiency.efficiency[type] > 0 then
		if threshold < 1 then
			displayed = true
			setup:addSimpleRow({ 
				Helper.unlockInfo(menu.unlocked.efficiency_type, name .. " (" .. ReadText(1001, 1) .. ReadText(1001, 120) .. " " .. hullpercent .. "%)"),
				Helper.createFontString(Helper.unlockInfo(menu.unlocked.efficiency_amount, Helper.round(menu.proddata.efficiency.efficiency[type] * (threshold * efffactor + effbonus - 1) * 100, 2)) .. " %", false, "right") 
			}, nil, {3, 1})
		end

		if #menu.proddata.efficiencyupgrades > 0 and menu.proddata.efficiency.efficiency then
			for _, effmodule in ipairs(menu.proddata.efficiencyupgrades) do
				displayed = true
				local name, efffactor, effbonus = GetMacroData(effmodule, "name", "efficiencyfactor", "efficiencybonus")
				setup:addSimpleRow({ 
					Helper.unlockInfo(menu.unlocked.efficiency_type, name),
					Helper.createFontString(Helper.unlockInfo(menu.unlocked.efficiency_amount, Helper.round(menu.proddata.efficiency.efficiency[type] * (threshold * efffactor + effbonus - threshold) * 100, 2)) .. " %", false, "right") 
				}, nil, {3, 1})
			end
		end
	end

	if menu.proddata.efficiency.specialist and menu.proddata.efficiency.specialist.specialist and menu.proddata.efficiency.specialist[type] > 0 then
		displayed = true
		local specialistname = GetComponentData(menu.proddata.efficiency.specialist.specialist, "name")
		setup:addSimpleRow({ 
			Helper.unlockInfo(menu.unlocked.efficiency_type, specialistname),
			Helper.createFontString(Helper.unlockInfo(menu.unlocked.efficiency_amount, Helper.round(menu.proddata.efficiency.specialist[type] * 100, 2)) .. " %", false, "right") 
		}, nil, {3, 1})
	end

	if menu.proddata.efficiency.sunlight and menu.proddata.efficiency.sunlight[type] > 0 then
		displayed = true
		setup:addSimpleRow({ 
			Helper.unlockInfo(menu.unlocked.efficiency_type, ReadText(1001, 2412)),
			Helper.createFontString(Helper.unlockInfo(menu.unlocked.efficiency_amount, Helper.round(menu.proddata.efficiency.sunlight[type] * 100, 2)) .. " %", false, "right") 
		}, nil, {3, 1})
	end

	if menu.proddata.efficiency.secondary and menu.proddata.efficiency.secondary[type] > 0 then
		displayed = true
		setup:addSimpleRow({  
			Helper.unlockInfo(menu.unlocked.efficiency_type, ReadText(1001, 1615)),
			Helper.createFontString(Helper.unlockInfo(menu.unlocked.efficiency_amount, Helper.round(menu.proddata.efficiency.secondary[type] * 100, 2)) .. " %", false, "right") 
		}, nil, {3, 1})
	end

	if not displayed then
		setup:addSimpleRow({  
			"-- " .. (type == "cycle" and ReadText(1001, 1623) or ReadText(1001, 1622)) .. " --"
		}, nil, {3, 1})
	end
end

function menu.updateWares(waretable, oldwaretable, headerrow, usage)
	if #waretable ~= 0 then
		for i, ware in ipairs(waretable) do
			if oldwaretable[i].amount ~= ware.amount then
				oldwaretable[i].amount = ware.amount
				Helper.updateCellText(menu.selecttable, headerrow + i, 3, ConvertIntegerString(ware.amount, true, 4, true) .. " / " .. ConvertIntegerString(GetWareProductionLimit(menu.container or menu.object, ware.ware), true, 4, true))
				Helper.updateCellText(menu.selecttable, headerrow + i, 4, ConvertIntegerString(usage * ware.cycle * 3600 / menu.proddata.cycletime, true, 4, true))
			end
		end
	end
end

init()
Author: jth 
X Rebirth version: 4.00
Station Production v0.05 Apr 06 2017
================================
Limits the production rate of station production modules

Please be aware that this is a beta release and that you should back up your save games before using it. Save games zip up very efficiently to about 7% of the original size.

Installation and Compatibility with other mods
==============================================
To install it extract and copy the "station_prod" folder to the "Steam\steamapps\common\X Rebirth\extensions" folder (create the extensions folder if you don't have it already) or subscribe to it from Steam and restart the game.

This mod interacts with the player via a modified production menu. It adds a production Limiting section at the bottom of the main screen. 

This mod patches Egosoft's NPC_Staff.xml and MainMenu.xml files and replaces menu_production.lua with production_menu_st_prod.lua and adds a completely new throttle_menu_st_prod.lua and config_menu_st_prod.lua
 
X Rebirth will save some of the variables and the state of the Station Production cues as part of a normal save game. 

If you decide not to use this mod any more then 
Either
	Go to the Configuration menu and optionally "Write Production limits to Logbook: Global" and then "Clear Production limiting: Global"
	Then remove the "station_prod" folder or unsubscribe it from Steam and restart the game.
Or
	Remove the "station_prod" folder or unsubscribe it from Steam and restart the game (leaves some config info behind in Manager and player BlackBoards).

=== How to Use ===
Select the Station whose production module you wish to limit and click on Details
Open up the Production modules section
Select the Production module that you want to limit and click on Details

The Production Limiting section lists all of the wares that this module can produce
Click on the configuration icon (spanner) to the left of the ware that you want to limit
Now use the slider in the Production Limiting menu to set the production limiting or brake factor. 0% does nothing and 100% will stop all production for this module
Click on OK to accept the limit or Back to abandon any changes
This ware will now be limited

To change your mind return to the Production Limiting screen at any time
Setting the limit to 0% and clicking on OK will remove a production limit

If you want to use the Configuration menu then click on the top left icon (spanner) on the Production Limiting menu

The Station Production Limiting mod :-
-----------------------------------
Limits individual production modules on player stations
Allows different limits to be placed on each production module on each station and across different player stations
Supports the URV Wharf, Turret and Missile Forges which produce multiple products by limiting one or more of the potential products
If a product is 100% limited then the production cycle still runs but the production module, on average, produces nothing and consumes nothing
Reduces consumption of Primary and Secondary wares to go with the reduction in Products
A production limit can be set at any time and will immediately return un-needed resources but the product totals (stock adjustment) will only update at the end of the production cycle
The Production Limiting menu works in the same way as the Encyclopedia menu and does not show the effect of Product Efficiency (Specialists etc) but it will take that into account when it adjusts the production quantities
The Production Limiting menu only shows Primary Resources due to space limitations but it will limit the use of secondary resources when it adjusts the production quantities
As resources cannot be produced or consumed in fractions of units this mod attempts to limits the factor to values that consume and create complete units of wares
Multi-lingual with support for Russian, French, Italian, Spanish, English and German

=== Configuration menu ===
Let you dynamically set the Debug level. 
The higher of the Global or Station setting takes effect for that station
If you set the Global Debug to 1 or 2 and look in the debug log then you will see some additional information that shows what is happening under the bonnet.
You will need a log file tailing program such as SnakeTail (http://snakenest.com/snaketail) to watch the end of the debug.log
Lets you log the configuration to the Logbook
Allows you to remove most of the data that this mod uses

=== How it works ===
This is a work around to the station managers production software

It relies on the station manager restocking some wares for it and adjusting the total production at the end of each batch as the production module software doesn't realise that some of its resources have been put back into stock

The full set of wares is still needed to start off a modified production cycle.
Within about 10 seconds of production starting the manager restocks a proportion of the wares which prevents the production module from producing the full amount of product and reduces the wares that it consumes
At the end of the production cycle the manager has to adjust the stock levels of product as the production module thinks that it has produced more than it actually has.
The result of the station managers work is to reduce the total amount of a specific ware that is produced and to reduce the wares that it consumes.

=== URV Wharf, Missile and Turret Forges ===
These produce more than one product by cycling betweeen the available products.
The products can be individually limited and the limits can be set at any time.
The limits for a specific product won't take effect until that product gets to its production slot.

=== Known issues ===
The full set of wares is still needed to start off a production cycle
The configuration data is wiped from a manager's BlackBoard when they are re-assigned. So make sure you save the info to the Logbook first ...
The Production Limiting menu slider value display has some rounding issues and only shows to the nearest integer
The debug information for the URV Wharf, Missile and Turret Forges does not correctly identify if a specific product is being produced - it reports them all as in the same state - cosmetic
Clearing the limits still leaves an empty list and a single blank value on each manager and the players BlackBoards. There is no lua Delete BlackBoard entry that I know of at the moment

=== ToDo ===
Module level debuging
Fully clean up player and Manager Blackboards
Alter the production rates displayed on the main production menu to show the effects of the production limit
Production Limit menu maybe put up a warning at the top of the screen if you are missing the resources to start a production cycle

=== Thanks ===
alexalsp and sparky for helping me find some of the bugs, doing some translation and providing some very useful feedback
All remaining bugs are mine

=== Diagnosing issues ===

These instructions are for Windows 7 64 bit, please adjust for other versions

Start X Rebirth in debug logging mode by adding  "-debug all -logfile debug.log" to the end of the command line

In my case this would look like

"C:\Program Files (x86)\Steam\steamapps\common\X Rebirth\XRebirth.exe" -debug all -logfile debug.log

That will create a debug.log file at the same level as your save game folder

=== Release history ===
- [06.04.2017 - V0.05 Public Beta]
	production_menu_st_prod.lua, limit_menu_st.prod, config_menu_st_prod gui - Compensate for format of version string changing in 4.10 RC3 and add extra paranoia when checking version to prevent crashing the GUI

- [07.03.2016 - V0.04 Public Beta]
	production_menu_st_prod.lua was always using the default production method even with OL stations which caused food to be displayed as a primary resource instead of Bofu. Although it displayed food while setting the Limiting factor it actually correctly returned Bofu so this was actually cosmetic. Added a work around that should cope with omicron, devries and xenon production methods

- [02.03.2016 - V0.03 Public Beta]
	renamed throttle_menu_st_prod.lua to limit_menu_st_prod.lua to line up better with mod naming
	Changed names within lua scripts to line up better with mod naming
	"increased paranoia" by doing more input and API return value checking in lua scripts. I hope this will help to cope with weird and wonderful game scenarios, duff stations, API calls that unexpectedly fail and other oddities

- [25.02.2016 - V0.02 Public Beta] 
	throttle_menu_st_prod.lua Add the minimum primary wares that are needed to start a production batch and a station Storage column
	throttle_menu_st_prod.lua Improve onUpdate to only update the screen if something has changed
	throttle_menu_st_prod.lua Add station storage to onUpdate
	throttle_menu_st_prod.lua, production_menu_st_prod.lua and config_menu_st_prod.lua menu.remotelog Compensate for changes to AddUITriggeredEvent parameters in 4.00 RC2
	production_menu_st_prod.lua Compensate for changes to gEncyclopedia_ware parameters in 4.00 RC2
	production_menu_st_prod.lua Incorporate Egosoft version of fix for onUpdate
	st_prod.xml St_Prod_Logger Compensate for changes to AddUITriggeredEvent parameters in 4.00 RC2
	
- [17.02.2016 - V0.01 Public Beta] 
	Initial Release 
	<?xml version="1.0" encoding="utf-8"?>
<content id="" name="" description="" author="jth" version="5" date="2017-04-06" save="0" lastupdate="">
  <dependency version="361"/>
  <text language="7" name="Station Production Limit" description="Limits production module output" author="jth"/>
  <text language="33" name="Station Production Limit" description="Station de limite de production" author="jth"/>
  <text language="34" name="Station Production Limit" description="Salida de límites de producción" author="jth"/>
  <text language="39" name="Station Production Limit" description="Salida de límites de producción" author="jth"/>
  <text language="44" name="Station Production Limit" description="Limits station production" author="jth"/>
  <text language="49" name="Station Production Limit" description="Station Produktionsbeschränkung" author="jth"/>
</content>
