Compare commits
10 Commits
aff82936a2
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| d38bb9b9dc | |||
| 402f543295 | |||
|
|
7fa0a96b3d | ||
|
|
95670693d5 | ||
|
|
d5fa6c94fc | ||
|
|
43ca190993 | ||
| 77db980e4f | |||
|
|
c38a55330c | ||
|
|
b6dea93a18 | ||
|
|
9893c2dada |
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
.history
|
||||||
|
|
||||||
|
Logistics/V5/web/node_modules
|
||||||
117
LICENSE
Normal file
117
LICENSE
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 2, June 1991
|
||||||
|
|
||||||
|
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||||
|
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.
|
||||||
|
|
||||||
|
We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software.
|
||||||
|
|
||||||
|
Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations.
|
||||||
|
|
||||||
|
Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and modification follow.
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you".
|
||||||
|
|
||||||
|
Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does.
|
||||||
|
|
||||||
|
1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program.
|
||||||
|
|
||||||
|
You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.
|
||||||
|
|
||||||
|
2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change.
|
||||||
|
|
||||||
|
b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License.
|
||||||
|
|
||||||
|
c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.)
|
||||||
|
|
||||||
|
These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.
|
||||||
|
|
||||||
|
Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program.
|
||||||
|
|
||||||
|
In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.
|
||||||
|
|
||||||
|
3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following:
|
||||||
|
|
||||||
|
a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
|
||||||
|
|
||||||
|
b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
|
||||||
|
|
||||||
|
c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.)
|
||||||
|
|
||||||
|
The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.
|
||||||
|
|
||||||
|
If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code.
|
||||||
|
|
||||||
|
4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
|
||||||
|
|
||||||
|
5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it.
|
||||||
|
|
||||||
|
6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License.
|
||||||
|
|
||||||
|
7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program.
|
||||||
|
|
||||||
|
If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances.
|
||||||
|
|
||||||
|
It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.
|
||||||
|
|
||||||
|
This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.
|
||||||
|
|
||||||
|
8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.
|
||||||
|
|
||||||
|
9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation.
|
||||||
|
|
||||||
|
10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.
|
||||||
|
|
||||||
|
NO WARRANTY
|
||||||
|
|
||||||
|
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
one line to give the program's name and an idea of what it does. Copyright (C) yyyy name of author
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program is interactive, make it output a short notice like this when it starts in an interactive mode:
|
||||||
|
|
||||||
|
Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names:
|
||||||
|
|
||||||
|
Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||||
|
|
||||||
|
signature of Ty Coon, 1 April 1989 Ty Coon, President of Vice
|
||||||
148
Logistics/V5/README.md
Normal file
148
Logistics/V5/README.md
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
# 物流系统 v5.0 文档
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
物流系统 v5.0 是一个高级物品和流体传输系统,支持多容器操作和缓存模式。系统通过配置文件管理任务,支持复杂的过滤规则和优先级设置。
|
||||||
|
|
||||||
|
## 核心功能
|
||||||
|
|
||||||
|
### 1. 传输类型
|
||||||
|
- **物品传输**:在容器间移动物品
|
||||||
|
- **流体传输**:在容器间移动流体
|
||||||
|
|
||||||
|
### 2. 缓存模式
|
||||||
|
- 适用于及作为输出也作为输入的容器
|
||||||
|
- 物品被送如时添加临时黑名单 这些物品不会被再次传输到别的容器
|
||||||
|
|
||||||
|
### 3. 过滤规则
|
||||||
|
- **白名单**:只允许特定物品传输
|
||||||
|
- **黑名单**:禁止特定物品传输
|
||||||
|
- **数量限制**:设置一次传输物品数量
|
||||||
|
|
||||||
|
### 4. 优先级系统
|
||||||
|
- 输出容器按优先级分组
|
||||||
|
- 高优先级容器优先接收物品
|
||||||
|
- 同优先级采用轮询机制
|
||||||
|
|
||||||
|
## 配置文件结构
|
||||||
|
配置文件存储在 `config.cfg` 中,包含以下部分:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
{
|
||||||
|
settings = {
|
||||||
|
logEnabled = false, -- 日志开关
|
||||||
|
zhlog = false, -- 中文日志开关
|
||||||
|
delay = 0.01 -- 轮询延迟(秒)
|
||||||
|
},
|
||||||
|
tasks = { -- 任务列表
|
||||||
|
-- 任务示例
|
||||||
|
{
|
||||||
|
type = "item", -- 或 "fluid"
|
||||||
|
input = {
|
||||||
|
["容器名"] = {
|
||||||
|
cache = true/false,
|
||||||
|
whitelist = {["物品名"] = {}},
|
||||||
|
blacklist = {["物品名"] = {}}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
output = {
|
||||||
|
["容器名"] = {
|
||||||
|
priority = 数字,
|
||||||
|
cache = true/false,
|
||||||
|
whitelist = {["物品名"] = {count = 数量}},
|
||||||
|
blacklist = {["物品名"] = {}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Tlist = {} -- 缓存传输列表
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用命令
|
||||||
|
|
||||||
|
### 基本命令
|
||||||
|
| 命令 | 描述 | 示例 |
|
||||||
|
|------|------|------|
|
||||||
|
| `add <JSON>` | 添加新任务 | `add {"type":"item","input":{"left":{...}},"output":{"right":{...}}}` |
|
||||||
|
| `list` | 列出所有任务 | `list` |
|
||||||
|
| `remove <索引>` | 移除任务 | `remove 1` |
|
||||||
|
| `set <key> <value>` | 修改设置 | `set delay 0.05` |
|
||||||
|
| `help` | 显示帮助信息 | `help` |
|
||||||
|
| `exit` | 退出程序 | `exit` |
|
||||||
|
|
||||||
|
### 设置命令
|
||||||
|
| 键 | 值 | 描述 |
|
||||||
|
|----|----|------|
|
||||||
|
| `log` | true/false | 启用/禁用日志 |
|
||||||
|
| `zh` | true/false | 启用/禁用中文日志 |
|
||||||
|
| `delay` | 数字 | 设置轮询延迟(秒) |
|
||||||
|
|
||||||
|
## 任务配置示例
|
||||||
|
|
||||||
|
### 物品传输任务
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "item",
|
||||||
|
"input": {
|
||||||
|
"left": {
|
||||||
|
"cache": true,
|
||||||
|
"whitelist": {
|
||||||
|
"minecraft:stone": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"output": {
|
||||||
|
"right": {
|
||||||
|
"priority": 1,
|
||||||
|
"whitelist": {
|
||||||
|
"minecraft:stone": {"count": 64}
|
||||||
|
},
|
||||||
|
"blacklist": {
|
||||||
|
"minecraft:dirt": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 流体传输任务
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "fluid",
|
||||||
|
"input": {
|
||||||
|
"tank_left": {
|
||||||
|
"whitelist": {
|
||||||
|
"water": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"output": {
|
||||||
|
"tank_right": {
|
||||||
|
"priority": 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 日志系统
|
||||||
|
- **日志级别**:
|
||||||
|
- SUCCESS (绿色):成功操作
|
||||||
|
- SKIPPED (橙色):跳过操作
|
||||||
|
- INFO (白色):一般信息
|
||||||
|
- WARNING (黄色):警告信息
|
||||||
|
- ERROR (红色):错误信息
|
||||||
|
- DEBUG (浅灰色):调试信息
|
||||||
|
|
||||||
|
- **日志格式**:
|
||||||
|
```
|
||||||
|
[时间] 级别: 消息
|
||||||
|
```
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
1. 确保所有容器名称正确且可访问
|
||||||
|
2. 确保所有容器链接到电脑的同一个调制解调器
|
||||||
|
3. 流体容器必须支持流体接口
|
||||||
|
4. 复杂的过滤规则可能影响性能
|
||||||
|
5. 轮询延迟设置过低可能导致CPU占用过高
|
||||||
|
|
||||||
|
系统通过高效的缓存机制和优先级处理,实现了复杂的多容器物流管理,适用于各种自动化场景。
|
||||||
947
Logistics/V5/startup.lua
Normal file
947
Logistics/V5/startup.lua
Normal file
@@ -0,0 +1,947 @@
|
|||||||
|
-- Logistics System v5.0 (Multi-Container & Cache Mode)
|
||||||
|
-- 支持带缓存的复杂容器间传输
|
||||||
|
-- 配置保存在 config.cfg
|
||||||
|
local configFile = "config.cfg"
|
||||||
|
local tasks = {}
|
||||||
|
local settings = {
|
||||||
|
logEnabled = false, -- 日志开关,默认关闭
|
||||||
|
zhlog = false, -- 中文日志开关,默认关闭
|
||||||
|
delay = 0.01 -- 轮询延迟,默认0.01秒
|
||||||
|
}
|
||||||
|
local Tlist = {} -- 缓存传输列表
|
||||||
|
local lastOutput = {} -- 记录每个任务每个优先级组上次使用的输出容器
|
||||||
|
|
||||||
|
-- 加载配置
|
||||||
|
local function loadConfig()
|
||||||
|
if fs.exists(configFile) then
|
||||||
|
local file = fs.open(configFile, "r")
|
||||||
|
local data = file.readAll()
|
||||||
|
file.close()
|
||||||
|
local config = textutils.unserialize(data) or {}
|
||||||
|
|
||||||
|
-- 加载设置
|
||||||
|
if config.settings then
|
||||||
|
settings.logEnabled = config.settings.logEnabled or settings.logEnabled
|
||||||
|
settings.zhlog = config.settings.zhlog or settings.zhlog
|
||||||
|
settings.delay = config.settings.delay or settings.delay
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 加载任务
|
||||||
|
tasks = config.tasks or {}
|
||||||
|
|
||||||
|
-- 加载传输列表
|
||||||
|
Tlist = config.Tlist or {}
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
loadConfig()
|
||||||
|
|
||||||
|
-- 保存配置
|
||||||
|
local function saveConfig()
|
||||||
|
local config = {
|
||||||
|
settings = {
|
||||||
|
logEnabled = settings.logEnabled,
|
||||||
|
zhlog = settings.zhlog,
|
||||||
|
delay = settings.delay
|
||||||
|
},
|
||||||
|
tasks = tasks,
|
||||||
|
Tlist = Tlist
|
||||||
|
}
|
||||||
|
|
||||||
|
local file = fs.open(configFile, "w")
|
||||||
|
file.write(textutils.serialize(config))
|
||||||
|
file.close()
|
||||||
|
end
|
||||||
|
|
||||||
|
if settings.zhlog then
|
||||||
|
printUtf8 = load(http.get("https://alist.liulikeji.cn/d/HFS/utf8ptrint.lua").readAll())()
|
||||||
|
term.clear()
|
||||||
|
term.setCursorPos(1,1)
|
||||||
|
else
|
||||||
|
function printUtf8(logMessage,color,bcolor)
|
||||||
|
term.setTextColour(color)
|
||||||
|
term.setBackgroundColour(bcolor)
|
||||||
|
print(logMessage)
|
||||||
|
term.setTextColour(colors.white)
|
||||||
|
term.setBackgroundColour(colors.black)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 日志级别定义
|
||||||
|
local LOG_LEVEL = {
|
||||||
|
SUCCESS = {name = "SUCCESS", color = colors.green},
|
||||||
|
SKIPPED = {name = "SKIPPED", color = colors.orange},
|
||||||
|
INFO = {name = "INFO", color = colors.white},
|
||||||
|
WARNING = {name = "WARNING", color = colors.yellow},
|
||||||
|
ERROR = {name = "ERROR", color = colors.red},
|
||||||
|
DEBUG = {name = "DEBUG", color = colors.lightGray}
|
||||||
|
}
|
||||||
|
|
||||||
|
-- 日志记录函数
|
||||||
|
local function log(level, zhMessage, enMessage)
|
||||||
|
if level ~= LOG_LEVEL.ERROR then
|
||||||
|
if not settings.logEnabled then return end
|
||||||
|
end
|
||||||
|
|
||||||
|
local message = settings.zhlog and zhMessage or enMessage
|
||||||
|
local timeStr = os.date("%H:%M:%S")
|
||||||
|
local levelName = level.name
|
||||||
|
local color = level.color
|
||||||
|
|
||||||
|
-- 格式化日志消息
|
||||||
|
local logMessage = string.format("[%s] %s: %s", timeStr, levelName, message)
|
||||||
|
|
||||||
|
-- 打印日志
|
||||||
|
printUtf8(logMessage, color, colors.black)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 命令输出函数(双语支持)
|
||||||
|
local function echo(zhMessage, enMessage)
|
||||||
|
local message = settings.zhlog and zhMessage or enMessage
|
||||||
|
printUtf8(message, colors.lightBlue, colors.black)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 初始化日志
|
||||||
|
log(LOG_LEVEL.INFO, "程序开始 - 物流系统 v5.0", "Program started - Turtle Logistics System v5.0")
|
||||||
|
|
||||||
|
-- 获取容器物品列表
|
||||||
|
local function getContainerItems(containerName)
|
||||||
|
local container = peripheral.wrap(containerName)
|
||||||
|
if not container then
|
||||||
|
log(LOG_LEVEL.ERROR, "错误: 容器不存在: "..containerName, "Error: Container not found: "..containerName)
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local items = {}
|
||||||
|
local containerList = container.list()
|
||||||
|
|
||||||
|
for slot, item in pairs(containerList) do
|
||||||
|
items[tostring(slot)] = {
|
||||||
|
count = item.count,
|
||||||
|
name = item.name
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
return items
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 比较两个物品列表是否相同
|
||||||
|
local function compareItems(items1, items2)
|
||||||
|
if not items1 or not items2 then return false end
|
||||||
|
|
||||||
|
-- 检查槽位数量
|
||||||
|
local count1 = 0
|
||||||
|
for _ in pairs(items1) do count1 = count1 + 1 end
|
||||||
|
local count2 = 0
|
||||||
|
for _ in pairs(items2) do count2 = count2 + 1 end
|
||||||
|
if count1 ~= count2 then return false end
|
||||||
|
|
||||||
|
-- 检查每个槽位
|
||||||
|
for slot, item1 in pairs(items1) do
|
||||||
|
local item2 = items2[slot]
|
||||||
|
if not item2 then return false end
|
||||||
|
if item1.name ~= item2.name or item1.count ~= item2.count then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 获取容器中不在Tlist中的物品
|
||||||
|
local function getNewItems(containerName)
|
||||||
|
local currentItems = getContainerItems(containerName)
|
||||||
|
if not currentItems then return nil end
|
||||||
|
|
||||||
|
local tlistItems = Tlist[containerName] or {}
|
||||||
|
local newItems = {}
|
||||||
|
|
||||||
|
for slot, item in pairs(currentItems) do
|
||||||
|
local tlistItem = tlistItems[slot]
|
||||||
|
if not tlistItem or tlistItem.name ~= item.name or tlistItem.count ~= item.count then
|
||||||
|
newItems[slot] = item
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return newItems
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 更新Tlist中的容器记录
|
||||||
|
local function updateTlist(containerName, items)
|
||||||
|
if not Tlist[containerName] then
|
||||||
|
Tlist[containerName] = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 只更新传入的物品槽位
|
||||||
|
for slot, item in pairs(items) do
|
||||||
|
Tlist[containerName][slot] = {
|
||||||
|
name = item.name,
|
||||||
|
count = item.count
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 清理Tlist中缺失的物品
|
||||||
|
local function cleanTlist(containerName)
|
||||||
|
if not Tlist[containerName] then return end
|
||||||
|
|
||||||
|
local currentItems = getContainerItems(containerName)
|
||||||
|
if not currentItems then return end
|
||||||
|
|
||||||
|
local toRemove = {}
|
||||||
|
|
||||||
|
-- 找出Tlist中存在但容器中不存在的物品
|
||||||
|
for slot, tlistItem in pairs(Tlist[containerName]) do
|
||||||
|
local currentItem = currentItems[slot]
|
||||||
|
if not currentItem or currentItem.name ~= tlistItem.name or currentItem.count ~= tlistItem.count then
|
||||||
|
table.insert(toRemove, slot)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 移除这些物品
|
||||||
|
for _, slot in ipairs(toRemove) do
|
||||||
|
Tlist[containerName][slot] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 辅助函数:检查物品是否通过过滤规则
|
||||||
|
local function isItemAllowed(itemName, containerConfig)
|
||||||
|
-- 如果有白名单,只允许白名单中的物品
|
||||||
|
if containerConfig.whitelist then
|
||||||
|
return containerConfig.whitelist[itemName] ~= nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 如果有黑名单,排除黑名单中的物品
|
||||||
|
if containerConfig.blacklist then
|
||||||
|
return containerConfig.blacklist[itemName] == nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 没有过滤规则时允许所有物品
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 获取物品的传输数量限制
|
||||||
|
local function getTransferLimit(itemName, containerConfig)
|
||||||
|
-- 检查白名单中是否有该物品的数量限制
|
||||||
|
if containerConfig.whitelist and containerConfig.whitelist[itemName] then
|
||||||
|
local countSetting = containerConfig.whitelist[itemName].count
|
||||||
|
if countSetting and countSetting > 0 then
|
||||||
|
return countSetting
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- 如果没有限制或限制为0,则返回nil表示传输全部
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 在 executeItemTask 函数中添加更多调试日志
|
||||||
|
local function executeItemTask(task, taskIndex)
|
||||||
|
log(LOG_LEVEL.INFO, "开始执行物品任务 #"..taskIndex, "Starting item transfer task #"..taskIndex)
|
||||||
|
|
||||||
|
-- 收集所有输入容器的物品
|
||||||
|
local allItemsToTransfer = {}
|
||||||
|
|
||||||
|
-- 处理输入容器
|
||||||
|
for inputName, inputConfig in pairs(task.input) do
|
||||||
|
log(LOG_LEVEL.DEBUG, "开始处理输入容器: "..inputName, "Starting processing input container: "..inputName)
|
||||||
|
|
||||||
|
local container = peripheral.wrap(inputName)
|
||||||
|
if not container then
|
||||||
|
log(LOG_LEVEL.ERROR, "错误: 输入容器不存在: "..inputName, "Error: Input container not found: "..inputName)
|
||||||
|
goto continue
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 缓存容器处理
|
||||||
|
if inputConfig.cache then
|
||||||
|
log(LOG_LEVEL.DEBUG, "容器 "..inputName.." 是缓存容器", "Container "..inputName.." is a cache container")
|
||||||
|
|
||||||
|
-- 清理Tlist中缺失的物品
|
||||||
|
cleanTlist(inputName)
|
||||||
|
|
||||||
|
-- 获取当前物品状态
|
||||||
|
local currentItems = getContainerItems(inputName)
|
||||||
|
if not currentItems then
|
||||||
|
log(LOG_LEVEL.DEBUG, "无法获取容器物品: "..inputName, "Failed to get container items: "..inputName)
|
||||||
|
goto continue
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 检查是否与Tlist完全相同
|
||||||
|
if compareItems(currentItems, Tlist[inputName] or {}) then
|
||||||
|
log(LOG_LEVEL.DEBUG, "容器内容与Tlist相同,跳过: "..inputName, "Container contents match Tlist, skipping: "..inputName)
|
||||||
|
goto continue
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 获取新物品(不在Tlist中的)
|
||||||
|
local newItems = getNewItems(inputName)
|
||||||
|
if not newItems or not next(newItems) then
|
||||||
|
log(LOG_LEVEL.DEBUG, "没有新物品,跳过: "..inputName, "No new items, skipping: "..inputName)
|
||||||
|
goto continue
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 添加到传输列表(应用输入容器过滤)
|
||||||
|
for slot, item in pairs(newItems) do
|
||||||
|
if isItemAllowed(item.name, inputConfig) then
|
||||||
|
-- 不再在输入容器限制流量
|
||||||
|
-- -- 获取传输数量限制
|
||||||
|
-- local transferLimit = getTransferLimit(item.name, inputConfig)
|
||||||
|
|
||||||
|
-- -- 检查是否满足传输条件
|
||||||
|
-- if transferLimit and item.count < transferLimit then
|
||||||
|
-- log(LOG_LEVEL.DEBUG, "物品 "..item.name.." 数量不足("..item.count.."<"..transferLimit.."),跳过传输",
|
||||||
|
-- "Item "..item.name.." insufficient quantity ("..item.count.."<"..transferLimit.."), skipping transfer")
|
||||||
|
-- goto continue_item
|
||||||
|
-- end
|
||||||
|
|
||||||
|
-- local transferCount = transferLimit or item.count
|
||||||
|
local transferCount = item.count
|
||||||
|
|
||||||
|
-- 如果数量大于0才添加
|
||||||
|
if transferCount > 0 then
|
||||||
|
log(LOG_LEVEL.DEBUG, "添加物品到传输列表: "..item.name.." x"..transferCount.." (槽位 "..slot..")",
|
||||||
|
"Adding item to transfer list: "..item.name.." x"..transferCount.." (slot "..slot..")")
|
||||||
|
table.insert(allItemsToTransfer, {
|
||||||
|
container = inputName,
|
||||||
|
slot = tonumber(slot),
|
||||||
|
name = item.name,
|
||||||
|
count = transferCount
|
||||||
|
})
|
||||||
|
else
|
||||||
|
log(LOG_LEVEL.DEBUG, "物品 "..item.name.." 数量不足,跳过传输",
|
||||||
|
"Item "..item.name.." insufficient quantity, skipping transfer")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
log(LOG_LEVEL.DEBUG, "物品 "..item.name.." 被输入容器 "..inputName.." 过滤",
|
||||||
|
"Item "..item.name.." filtered by input container "..inputName)
|
||||||
|
end
|
||||||
|
|
||||||
|
::continue_item::
|
||||||
|
end
|
||||||
|
else
|
||||||
|
log(LOG_LEVEL.DEBUG, "容器 "..inputName.." 不是缓存容器", "Container "..inputName.." is not a cache container")
|
||||||
|
local items = getContainerItems(inputName)
|
||||||
|
if not items then
|
||||||
|
log(LOG_LEVEL.DEBUG, "无法获取容器物品: "..inputName, "Failed to get container items: "..inputName)
|
||||||
|
goto continue
|
||||||
|
end
|
||||||
|
|
||||||
|
for slot, item in pairs(items) do
|
||||||
|
-- 应用输入容器过滤
|
||||||
|
if isItemAllowed(item.name, inputConfig) then
|
||||||
|
-- 不再在输入容器限制流量
|
||||||
|
-- 获取传输数量限制
|
||||||
|
-- local transferLimit = getTransferLimit(item.name, inputConfig)
|
||||||
|
|
||||||
|
-- -- 检查是否满足传输条件
|
||||||
|
-- if transferLimit and item.count < transferLimit then
|
||||||
|
-- log(LOG_LEVEL.DEBUG, "物品 "..item.name.." 数量不足("..item.count.."<"..transferLimit.."),跳过传输",
|
||||||
|
-- "Item "..item.name.." insufficient quantity ("..item.count.."<"..transferLimit.."), skipping transfer")
|
||||||
|
-- goto continue_item_non_cache
|
||||||
|
-- end
|
||||||
|
|
||||||
|
-- local transferCount = transferLimit or item.count
|
||||||
|
local transferCount = item.count
|
||||||
|
|
||||||
|
-- 如果数量大于0才添加
|
||||||
|
if transferCount > 0 then
|
||||||
|
log(LOG_LEVEL.DEBUG, "添加物品到传输列表: "..item.name.." x"..transferCount.." (槽位 "..slot..")",
|
||||||
|
"Adding item to transfer list: "..item.name.." x"..transferCount.." (slot "..slot..")")
|
||||||
|
table.insert(allItemsToTransfer, {
|
||||||
|
container = inputName,
|
||||||
|
slot = tonumber(slot),
|
||||||
|
name = item.name,
|
||||||
|
count = transferCount
|
||||||
|
})
|
||||||
|
else
|
||||||
|
log(LOG_LEVEL.DEBUG, "物品 "..item.name.." 数量不足,跳过传输",
|
||||||
|
"Item "..item.name.." insufficient quantity, skipping transfer")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
log(LOG_LEVEL.DEBUG, "物品 "..item.name.." 被输入容器 "..inputName.." 过滤",
|
||||||
|
"Item "..item.name.." filtered by input container "..inputName)
|
||||||
|
end
|
||||||
|
|
||||||
|
::continue_item_non_cache::
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
::continue::
|
||||||
|
end
|
||||||
|
|
||||||
|
if #allItemsToTransfer == 0 then
|
||||||
|
log(LOG_LEVEL.SKIPPED, "没有物品需要传输", "No items to transfer")
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
log(LOG_LEVEL.INFO, "找到 "..#allItemsToTransfer.." 个物品需要传输", "Found "..#allItemsToTransfer.." items to transfer")
|
||||||
|
|
||||||
|
-- 按优先级分组输出容器
|
||||||
|
local outputGroups = {}
|
||||||
|
for outputName, outputConfig in pairs(task.output) do
|
||||||
|
local priority = outputConfig.priority or 1
|
||||||
|
if not outputGroups[priority] then
|
||||||
|
outputGroups[priority] = {}
|
||||||
|
end
|
||||||
|
table.insert(outputGroups[priority], {
|
||||||
|
name = outputName,
|
||||||
|
config = outputConfig
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 按优先级排序(从高到低)
|
||||||
|
local sortedPriorities = {}
|
||||||
|
for priority in pairs(outputGroups) do
|
||||||
|
table.insert(sortedPriorities, priority)
|
||||||
|
end
|
||||||
|
table.sort(sortedPriorities, function(a, b) return a > b end)
|
||||||
|
|
||||||
|
-- 传输物品
|
||||||
|
local transferred = false
|
||||||
|
|
||||||
|
for _, itemData in ipairs(allItemsToTransfer) do
|
||||||
|
log(LOG_LEVEL.DEBUG, "尝试传输物品: "..itemData.name.." x"..itemData.count.." (源容器: "..itemData.container..", 槽位: "..itemData.slot..")",
|
||||||
|
"Attempting to transfer item: "..itemData.name.." x"..itemData.count.." (source: "..itemData.container..", slot: "..itemData.slot..")")
|
||||||
|
|
||||||
|
local sourceContainer = peripheral.wrap(itemData.container)
|
||||||
|
if not sourceContainer then
|
||||||
|
log(LOG_LEVEL.ERROR, "错误: 源容器不存在: "..itemData.container, "Error: Source container not found: "..itemData.container)
|
||||||
|
goto next_item
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 尝试推送到输出容器
|
||||||
|
local pushed = false
|
||||||
|
|
||||||
|
-- 按优先级尝试输出
|
||||||
|
for _, priority in ipairs(sortedPriorities) do
|
||||||
|
local group = outputGroups[priority]
|
||||||
|
|
||||||
|
-- 轮询输出
|
||||||
|
local key = taskIndex.."_"..priority -- 使用任务索引和优先级作为唯一键
|
||||||
|
log(LOG_LEVEL.DEBUG, "输出容器键: "..key, "Output container key: "..key)
|
||||||
|
|
||||||
|
local startIdx = lastOutput[key] or 1
|
||||||
|
local idx = startIdx
|
||||||
|
local attempts = 0
|
||||||
|
|
||||||
|
repeat
|
||||||
|
log(LOG_LEVEL.DEBUG, "输出容器索引: "..tostring(idx).." 容器组大小: "..tostring(#group),"Output container index: "..tostring(idx).." group size: "..tostring(#group))
|
||||||
|
local output = group[idx]
|
||||||
|
log(LOG_LEVEL.DEBUG, "尝试输出容器: "..output.name.." (优先级 "..priority..", 尝试 "..attempts.."/"..#group..")",
|
||||||
|
"Attempting output container: "..output.name.." (priority "..priority..", attempt "..attempts.."/"..#group..")")
|
||||||
|
|
||||||
|
local outputContainer = peripheral.wrap(output.name)
|
||||||
|
|
||||||
|
if outputContainer then
|
||||||
|
-- 应用输出容器过滤
|
||||||
|
if not isItemAllowed(itemData.name, output.config) then
|
||||||
|
log(LOG_LEVEL.DEBUG, "物品 "..itemData.name.." 被输出容器 "..output.name.." 过滤",
|
||||||
|
"Item "..itemData.name.." filtered by output container "..output.name)
|
||||||
|
goto next_output
|
||||||
|
end
|
||||||
|
|
||||||
|
local transferLimit = getTransferLimit(itemData.name, output.config)
|
||||||
|
|
||||||
|
-- 检查是否满足传输条件
|
||||||
|
if transferLimit and itemData.count < transferLimit then
|
||||||
|
log(LOG_LEVEL.DEBUG, "物品 "..itemData.name.." 数量不足("..itemData.count.."<"..transferLimit.."),跳过传输",
|
||||||
|
"Item "..itemData.name.." insufficient quantity ("..itemData.count.."<"..transferLimit.."), skipping transfer")
|
||||||
|
goto next_output
|
||||||
|
end
|
||||||
|
|
||||||
|
local transferCount = transferLimit or itemData.count
|
||||||
|
|
||||||
|
log(LOG_LEVEL.DEBUG, "尝试推送到 "..output.name.." (优先级 "..priority..")", "Attempting push to "..output.name.." (priority "..priority..")")
|
||||||
|
|
||||||
|
-- 推送物品
|
||||||
|
local pushedCount = sourceContainer.pushItems(
|
||||||
|
output.name,
|
||||||
|
itemData.slot,
|
||||||
|
transferCount
|
||||||
|
)
|
||||||
|
|
||||||
|
if pushedCount > 0 then
|
||||||
|
log(LOG_LEVEL.SUCCESS, "成功推送 "..pushedCount.." 个 "..itemData.name.." 到 "..output.name, "Successfully pushed "..pushedCount.." "..itemData.name.." to "..output.name)
|
||||||
|
pushed = true
|
||||||
|
transferred = true
|
||||||
|
|
||||||
|
-- 更新输出容器的Tlist(如果是缓存容器)
|
||||||
|
if output.config.cache then
|
||||||
|
log(LOG_LEVEL.DEBUG, "更新输出容器Tlist: "..output.name, "Updating output container Tlist: "..output.name)
|
||||||
|
if not Tlist[output.name] then
|
||||||
|
Tlist[output.name] = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 获取输出容器当前物品
|
||||||
|
local outputItems = getContainerItems(output.name)
|
||||||
|
if outputItems then
|
||||||
|
-- 只更新传输的物品槽位
|
||||||
|
for slot, item in pairs(outputItems) do
|
||||||
|
if item.name == itemData.name then
|
||||||
|
Tlist[output.name][slot] = {
|
||||||
|
name = item.name,
|
||||||
|
count = item.count
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 更新输入容器的Tlist(如果是缓存容器)
|
||||||
|
if task.input[itemData.container] and task.input[itemData.container].cache then
|
||||||
|
log(LOG_LEVEL.DEBUG, "更新输入容器Tlist: "..itemData.container, "Updating input container Tlist: "..itemData.container)
|
||||||
|
updateTlist(itemData.container, {
|
||||||
|
[tostring(itemData.slot)] = {
|
||||||
|
name = itemData.name,
|
||||||
|
count = itemData.count - pushedCount
|
||||||
|
}
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 更新轮询索引
|
||||||
|
log(LOG_LEVEL.DEBUG, "轮询: idx:" .. tostring(idx) .. " #group:" .. tostring(#group))
|
||||||
|
lastOutput[key] = idx % #group + 1
|
||||||
|
break
|
||||||
|
else
|
||||||
|
log(LOG_LEVEL.WARNING, "推送失败到 "..output.name.." (可能容器已满或无法接收)",
|
||||||
|
"Push failed to "..output.name.." (container may be full or unable to receive)")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
log(LOG_LEVEL.ERROR, "输出容器不存在或无法访问: "..output.name, "Output container not found or inaccessible: "..output.name)
|
||||||
|
end
|
||||||
|
|
||||||
|
::next_output::
|
||||||
|
idx = idx % #group + 1
|
||||||
|
attempts = attempts + 1
|
||||||
|
until attempts >= #group
|
||||||
|
|
||||||
|
if pushed then break end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not pushed then
|
||||||
|
log(LOG_LEVEL.WARNING, "警告: 无法传输物品 "..itemData.name.." x"..itemData.count.." (所有输出容器尝试失败)",
|
||||||
|
"Warning: Failed to transfer item "..itemData.name.." x"..itemData.count.." (all output containers failed)")
|
||||||
|
end
|
||||||
|
|
||||||
|
::next_item::
|
||||||
|
end
|
||||||
|
|
||||||
|
if not transferred then
|
||||||
|
log(LOG_LEVEL.DEBUG, "所有物品传输尝试失败", "All item transfer attempts failed")
|
||||||
|
end
|
||||||
|
|
||||||
|
return transferred
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 执行流体传输任务
|
||||||
|
local function executeFluidTask(task, taskIndex)
|
||||||
|
log(LOG_LEVEL.INFO, "开始执行流体任务 #"..taskIndex, "Starting fluid transfer task #"..taskIndex)
|
||||||
|
|
||||||
|
local transferred = false
|
||||||
|
|
||||||
|
-- 处理输入容器
|
||||||
|
for inputName, inputConfig in pairs(task.input) do
|
||||||
|
log(LOG_LEVEL.INFO, "处理输入容器: "..inputName, "Processing input container: "..inputName)
|
||||||
|
|
||||||
|
local source = peripheral.wrap(inputName)
|
||||||
|
if not source then
|
||||||
|
log(LOG_LEVEL.ERROR, "错误: 源容器不存在: "..inputName, "Error: Source container not found: "..inputName)
|
||||||
|
goto continue
|
||||||
|
end
|
||||||
|
|
||||||
|
if not source.tanks then
|
||||||
|
log(LOG_LEVEL.WARNING, "警告: 容器不支持流体: "..inputName.." - 跳过", "Warning: Container does not support fluids: "..inputName.." - skipping")
|
||||||
|
goto continue
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 获取流体槽 - 使用 pairs 而不是 # 来计算数量
|
||||||
|
local tanks = source.tanks()
|
||||||
|
if not tanks then
|
||||||
|
log(LOG_LEVEL.INFO, "容器中没有流体槽: "..inputName, "No fluid tanks in container: "..inputName)
|
||||||
|
goto continue
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 正确计算流体槽数量
|
||||||
|
local tankCount = 0
|
||||||
|
for _ in pairs(tanks) do tankCount = tankCount + 1 end
|
||||||
|
|
||||||
|
if tankCount == 0 then
|
||||||
|
log(LOG_LEVEL.INFO, "容器中没有流体槽: "..inputName, "No fluid tanks in container: "..inputName)
|
||||||
|
goto continue
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 使用 pairs 遍历所有流体槽
|
||||||
|
for tankIndex, tank in pairs(tanks) do
|
||||||
|
-- 确保 tank 是有效的表
|
||||||
|
if type(tank) == "table" and tank.name and tank.amount then
|
||||||
|
-- 应用输入容器过滤
|
||||||
|
if tank.amount > 0 and isItemAllowed(tank.name, inputConfig) then
|
||||||
|
log(LOG_LEVEL.INFO, "找到可传输流体: "..tank.name.." ("..tank.amount.."mB)", "Found transferable fluid: "..tank.name.." ("..tank.amount.."mB)")
|
||||||
|
|
||||||
|
-- 按优先级分组输出容器
|
||||||
|
local outputGroups = {}
|
||||||
|
for outputName, outputConfig in pairs(task.output) do
|
||||||
|
local priority = outputConfig.priority or 1
|
||||||
|
if not outputGroups[priority] then
|
||||||
|
outputGroups[priority] = {}
|
||||||
|
end
|
||||||
|
table.insert(outputGroups[priority], {
|
||||||
|
name = outputName,
|
||||||
|
config = outputConfig
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 按优先级排序(从高到低)
|
||||||
|
local sortedPriorities = {}
|
||||||
|
for priority in pairs(outputGroups) do
|
||||||
|
table.insert(sortedPriorities, priority)
|
||||||
|
end
|
||||||
|
table.sort(sortedPriorities, function(a, b) return a > b end)
|
||||||
|
|
||||||
|
-- 尝试传输流体
|
||||||
|
local pushed = false
|
||||||
|
local amountToTransfer = tank.amount
|
||||||
|
|
||||||
|
for _, priority in ipairs(sortedPriorities) do
|
||||||
|
local group = outputGroups[priority]
|
||||||
|
|
||||||
|
-- 轮询输出
|
||||||
|
local key = taskIndex.."_"..priority -- 使用任务索引和优先级作为唯一键
|
||||||
|
local startIdx = lastOutput[key] or 1
|
||||||
|
local idx = startIdx
|
||||||
|
local attempts = 0
|
||||||
|
|
||||||
|
repeat
|
||||||
|
local output = group[idx]
|
||||||
|
local destination = peripheral.wrap(output.name)
|
||||||
|
|
||||||
|
if destination and destination.tanks then
|
||||||
|
-- 应用输出容器过滤
|
||||||
|
if not isItemAllowed(tank.name, output.config) then
|
||||||
|
log(LOG_LEVEL.INFO, "流体 "..tank.name.." 被输出容器 "..output.name.." 过滤",
|
||||||
|
"Fluid "..tank.name.." filtered by output container "..output.name)
|
||||||
|
goto next_output
|
||||||
|
end
|
||||||
|
|
||||||
|
log(LOG_LEVEL.INFO, "尝试推送到 "..output.name.." (优先级 "..priority..")", "Attempting push to "..output.name.." (priority "..priority..")")
|
||||||
|
|
||||||
|
-- 推送流体
|
||||||
|
local pushedAmount = source.pushFluid(
|
||||||
|
output.name,
|
||||||
|
amountToTransfer,
|
||||||
|
tank.name,
|
||||||
|
tankIndex
|
||||||
|
)
|
||||||
|
|
||||||
|
if pushedAmount > 0 then
|
||||||
|
log(LOG_LEVEL.SUCCESS, "成功推送 "..pushedAmount.."mB "..tank.name.." 到 "..output.name, "Successfully pushed "..pushedAmount.."mB "..tank.name.." to "..output.name)
|
||||||
|
pushed = true
|
||||||
|
transferred = true
|
||||||
|
amountToTransfer = amountToTransfer - pushedAmount
|
||||||
|
|
||||||
|
-- 更新输出容器的Tlist(如果是缓存容器)
|
||||||
|
if output.config.cache then
|
||||||
|
log(LOG_LEVEL.DEBUG, "更新输出容器Tlist: "..output.name, "Updating output container Tlist: "..output.name)
|
||||||
|
if not Tlist[output.name] then
|
||||||
|
Tlist[output.name] = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 获取输出容器当前流体
|
||||||
|
local destTanks = destination.tanks()
|
||||||
|
if destTanks then
|
||||||
|
for _, destTank in pairs(destTanks) do
|
||||||
|
if destTank.name == tank.name then
|
||||||
|
Tlist[output.name][tank.name] = {
|
||||||
|
amount = destTank.amount
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 更新轮询索引
|
||||||
|
lastOutput[key] = idx % #group + 1
|
||||||
|
else
|
||||||
|
log(LOG_LEVEL.WARNING, "推送失败到 "..output.name, "Push failed to "..output.name)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if not destination then
|
||||||
|
log(LOG_LEVEL.ERROR, "错误: 输出容器不存在: "..output.name, "Error: Output container not found: "..output.name)
|
||||||
|
else
|
||||||
|
log(LOG_LEVEL.WARNING, "错误: 输出容器不支持流体: "..output.name, "Error: Output container does not support fluids: "..output.name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
::next_output::
|
||||||
|
idx = idx % #group + 1
|
||||||
|
attempts = attempts + 1
|
||||||
|
until attempts >= #group or amountToTransfer <= 0
|
||||||
|
|
||||||
|
if amountToTransfer <= 0 then break end
|
||||||
|
end
|
||||||
|
|
||||||
|
if amountToTransfer > 0 then
|
||||||
|
log(LOG_LEVEL.WARNING, string.format("警告: 仍有 %dmB %s 未传输", amountToTransfer, tank.name),
|
||||||
|
string.format("Warning: Still %dmB %s not transferred", amountToTransfer, tank.name))
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if tank.amount == 0 then
|
||||||
|
log(LOG_LEVEL.INFO, "槽位 "..tankIndex.." 无流体", "Tank "..tankIndex.." has no fluid")
|
||||||
|
else
|
||||||
|
log(LOG_LEVEL.INFO, "流体 "..tank.name.." 被输入容器 "..inputName.." 过滤",
|
||||||
|
"Fluid "..tank.name.." filtered by input container "..inputName)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
log(LOG_LEVEL.WARNING, "警告: 槽位 "..tankIndex.." 的数据无效", "Warning: Invalid data for tank "..tankIndex)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
::continue::
|
||||||
|
end
|
||||||
|
|
||||||
|
return transferred
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 执行任务
|
||||||
|
local function executeTask(task, taskIndex)
|
||||||
|
if task.type == "item" then
|
||||||
|
return executeItemTask(task, taskIndex)
|
||||||
|
elseif task.type == "fluid" then
|
||||||
|
return executeFluidTask(task, taskIndex)
|
||||||
|
else
|
||||||
|
log(LOG_LEVEL.ERROR, "错误: 未知任务类型: "..(task.type or "nil"), "Error: Unknown task type: "..(task.type or "nil"))
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 主任务循环
|
||||||
|
local function taskLoop()
|
||||||
|
log(LOG_LEVEL.INFO, "任务循环开始", "Task loop started")
|
||||||
|
while true do
|
||||||
|
for i, task in ipairs(tasks) do
|
||||||
|
log(LOG_LEVEL.INFO, "开始任务 #"..i, "Starting task #"..i)
|
||||||
|
local success, result = pcall(executeTask, task, i)
|
||||||
|
if not success then
|
||||||
|
log(LOG_LEVEL.ERROR, "任务 #"..i.." 错误: "..tostring(result), "Task #"..i.." error: "..tostring(result))
|
||||||
|
elseif result then
|
||||||
|
log(LOG_LEVEL.SUCCESS, "任务 #"..i.." 成功执行", "Task #"..i.." executed successfully")
|
||||||
|
else
|
||||||
|
log(LOG_LEVEL.SKIPPED, "任务 #"..i.." 未执行传输", "Task #"..i.." no transfer performed")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
os.sleep(settings.delay)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 命令处理
|
||||||
|
local function processCommand(cmd)
|
||||||
|
local args = {}
|
||||||
|
for word in cmd:gmatch("%S+") do
|
||||||
|
table.insert(args, word)
|
||||||
|
end
|
||||||
|
|
||||||
|
if #args == 0 then return end
|
||||||
|
|
||||||
|
local command = args[1]:lower()
|
||||||
|
|
||||||
|
if command == "add" and #args >= 2 then
|
||||||
|
-- 合并参数为JSON字符串
|
||||||
|
local jsonStr = table.concat(args, " ", 2)
|
||||||
|
local success, task = pcall(textutils.unserializeJSON, jsonStr)
|
||||||
|
|
||||||
|
if success and type(task) == "table" then
|
||||||
|
-- 验证任务类型
|
||||||
|
if not task.type then
|
||||||
|
echo("错误: 任务缺少类型字段", "Error: Task missing type field")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if task.type ~= "item" and task.type ~= "fluid" then
|
||||||
|
echo("错误: 无效的任务类型: "..task.type, "Error: Invalid task type: "..task.type)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 验证输入输出
|
||||||
|
if not task.input or not task.output then
|
||||||
|
echo("错误: 任务缺少输入或输出字段", "Error: Task missing input or output fields")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(tasks, task)
|
||||||
|
saveConfig()
|
||||||
|
echo("任务添加成功", "Task added successfully")
|
||||||
|
else
|
||||||
|
echo("错误: 无效的JSON格式", "Error: Invalid JSON format")
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif command == "list" then
|
||||||
|
if #tasks == 0 then
|
||||||
|
echo("没有配置任务", "No tasks configured")
|
||||||
|
else
|
||||||
|
for i, task in ipairs(tasks) do
|
||||||
|
echo("任务 #"..i.." ("..task.type..")", "Task #"..i.." ("..task.type..")")
|
||||||
|
echo(" 输入容器:", " Input containers:")
|
||||||
|
for name, config in pairs(task.input) do
|
||||||
|
local cacheStr = config.cache and "缓存" or "非缓存"
|
||||||
|
local cacheStrEn = config.cache and "cache" or "non-cache"
|
||||||
|
|
||||||
|
-- 显示过滤规则
|
||||||
|
if config.whitelist then
|
||||||
|
local items = {}
|
||||||
|
for itemName, itemConfig in pairs(config.whitelist) do
|
||||||
|
if itemConfig.count and itemConfig.count > 0 then
|
||||||
|
table.insert(items, itemName.." (count="..itemConfig.count..")")
|
||||||
|
else
|
||||||
|
table.insert(items, itemName)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
echo(" "..name.." ("..cacheStr..", 白名单: "..table.concat(items, ", ")..")",
|
||||||
|
" "..name.." ("..cacheStrEn..", whitelist: "..table.concat(items, ", ")..")")
|
||||||
|
elseif config.blacklist then
|
||||||
|
local items = {}
|
||||||
|
for itemName in pairs(config.blacklist) do
|
||||||
|
table.insert(items, itemName)
|
||||||
|
end
|
||||||
|
echo(" "..name.." ("..cacheStr..", 黑名单: "..table.concat(items, ", ")..")",
|
||||||
|
" "..name.." ("..cacheStrEn..", blacklist: "..table.concat(items, ", ")..")")
|
||||||
|
else
|
||||||
|
echo(" "..name.." ("..cacheStr..")", " "..name.." ("..cacheStrEn..")")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
echo(" 输出容器:", " Output containers:")
|
||||||
|
for name, config in pairs(task.output) do
|
||||||
|
local cacheStr = config.cache and "缓存" or "非缓存"
|
||||||
|
local cacheStrEn = config.cache and "cache" or "non-cache"
|
||||||
|
local priority = config.priority or 1
|
||||||
|
|
||||||
|
-- 显示过滤规则
|
||||||
|
if config.whitelist then
|
||||||
|
local items = {}
|
||||||
|
for itemName, itemConfig in pairs(config.whitelist) do
|
||||||
|
if itemConfig.count and itemConfig.count > 0 then
|
||||||
|
table.insert(items, itemName.." (count="..itemConfig.count..")")
|
||||||
|
else
|
||||||
|
table.insert(items, itemName)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
echo(" "..name.." (优先级:"..priority..", "..cacheStr..", 白名单: "..table.concat(items, ", ")..")",
|
||||||
|
" "..name.." (priority:"..priority..", "..cacheStrEn..", whitelist: "..table.concat(items, ", ")..")")
|
||||||
|
elseif config.blacklist then
|
||||||
|
local items = {}
|
||||||
|
for itemName in pairs(config.blacklist) do
|
||||||
|
table.insert(items, itemName)
|
||||||
|
end
|
||||||
|
echo(" "..name.." (优先级:"..priority..", "..cacheStr..", 黑名单: "..table.concat(items, ", ")..")",
|
||||||
|
" "..name.." (priority:"..priority..", "..cacheStrEn..", blacklist: "..table.concat(items, ", ")..")")
|
||||||
|
else
|
||||||
|
echo(" "..name.." (优先级:"..priority..", "..cacheStr..")",
|
||||||
|
" "..name.." (priority:"..priority..", "..cacheStrEn..")")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif command == "remove" and #args >= 2 then
|
||||||
|
local index = tonumber(args[2])
|
||||||
|
if index and tasks[index] then
|
||||||
|
table.remove(tasks, index)
|
||||||
|
saveConfig()
|
||||||
|
echo("任务 "..index.." 已移除", "Task "..index.." removed")
|
||||||
|
else
|
||||||
|
echo("无效的任务索引", "Invalid task index")
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif command == "log" then
|
||||||
|
if #args >= 2 then
|
||||||
|
if args[2]:lower() == "on" then
|
||||||
|
settings.logEnabled = true
|
||||||
|
saveConfig()
|
||||||
|
echo("日志已启用", "Logging enabled")
|
||||||
|
elseif args[2]:lower() == "off" then
|
||||||
|
settings.logEnabled = false
|
||||||
|
saveConfig()
|
||||||
|
echo("日志已禁用", "Logging disabled")
|
||||||
|
else
|
||||||
|
echo("无效参数,使用 'log on' 或 'log off'", "Invalid argument, use 'log on' or 'log off'")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
echo("当前日志状态: "..(settings.logEnabled and "启用" or "禁用"),
|
||||||
|
"Current log status: "..(settings.logEnabled and "enabled" or "disabled"))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 添加 set 命令
|
||||||
|
elseif command == "set" and #args >= 3 then
|
||||||
|
local key = args[2]:lower()
|
||||||
|
local value = args[3]
|
||||||
|
|
||||||
|
if key == "log" then
|
||||||
|
if value == "true" then
|
||||||
|
settings.logEnabled = true
|
||||||
|
saveConfig()
|
||||||
|
echo("日志已启用", "Logging enabled")
|
||||||
|
elseif value == "false" then
|
||||||
|
settings.logEnabled = false
|
||||||
|
saveConfig()
|
||||||
|
echo("日志已禁用", "Logging disabled")
|
||||||
|
else
|
||||||
|
echo("无效值,使用 'true' 或 'false'", "Invalid value, use 'true' or 'false'")
|
||||||
|
end
|
||||||
|
elseif key == "zh" then
|
||||||
|
if value == "true" then
|
||||||
|
settings.zhlog = true
|
||||||
|
saveConfig()
|
||||||
|
echo("中文日志已启用", "Chinese log enabled")
|
||||||
|
elseif value == "false" then
|
||||||
|
settings.zhlog = false
|
||||||
|
saveConfig()
|
||||||
|
echo("中文日志已禁用", "Chinese log disabled")
|
||||||
|
else
|
||||||
|
echo("无效值,使用 'true' 或 'false'", "Invalid value, use 'true' or 'false'")
|
||||||
|
end
|
||||||
|
elseif key == "delay" then
|
||||||
|
local numValue = tonumber(value)
|
||||||
|
if numValue and numValue > 0 then
|
||||||
|
settings.delay = numValue
|
||||||
|
saveConfig()
|
||||||
|
echo("轮询延迟已设置为: "..numValue.." 秒", "Polling delay set to: "..numValue.." seconds")
|
||||||
|
else
|
||||||
|
echo("无效值,请输入大于0的数字", "Invalid value, please enter a number greater than 0")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
echo("无效设置项,可用选项: logEnabled, zhlog, delay", "Invalid setting, available options: logEnabled, zhlog, delay")
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif command == "help" then
|
||||||
|
echo("可用命令:", "Available commands:")
|
||||||
|
echo("add <JSON任务> - 添加新任务", "add <JSON task> - Add new task")
|
||||||
|
echo(" 示例: add {\"type\":\"item\",\"input\":{\"left\":{\"cache\":true,\"whitelist\":{\"minecraft:stone\":{\"count\":64}}}},\"output\":{\"right\":{\"priority\":1,\"blacklist\":{\"minecraft:dirt\":{}}}}",
|
||||||
|
" Example: add {\"type\":\"item\",\"input\":{\"left\":{\"cache\":true,\"whitelist\":{\"minecraft:stone\":{\"count\":64}}}},\"output\":{\"right\":{\"priority\":1,\"blacklist\":{\"minecraft:dirt\":{}}}}")
|
||||||
|
echo("list - 列出所有任务", "list - List all tasks")
|
||||||
|
echo("remove <索引> - 移除任务", "remove <index> - Remove task")
|
||||||
|
echo("log [on|off] - 启用/禁用日志", "log [on|off] - Enable/disable logging")
|
||||||
|
echo("set <key> <value> - 修改设置", "set <key> <value> - Modify settings")
|
||||||
|
echo(" 可用键: logEnabled (true/false), zhlog (true/false), delay (数字)", " Available keys: logEnabled (true/false), zhlog (true/false), delay (number)")
|
||||||
|
echo("exit - 退出程序", "exit - Exit program")
|
||||||
|
|
||||||
|
elseif command == "exit" then
|
||||||
|
echo("正在退出程序", "Exiting program")
|
||||||
|
os.sleep(0.5)
|
||||||
|
error("程序终止", 0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 初始化
|
||||||
|
|
||||||
|
echo("物流系统 v5.0 已启动。输入 'help' 查看命令。",
|
||||||
|
"Turtle Logistics System v5.0 started. Type 'help' for commands.")
|
||||||
|
|
||||||
|
-- 主循环
|
||||||
|
parallel.waitForAll(
|
||||||
|
function()
|
||||||
|
while true do
|
||||||
|
write("> ")
|
||||||
|
local command = read()
|
||||||
|
processCommand(command)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
taskLoop
|
||||||
|
)
|
||||||
935
Logistics/V5/web/1.txt
Normal file
935
Logistics/V5/web/1.txt
Normal file
@@ -0,0 +1,935 @@
|
|||||||
|
-- Turtle Logistics System v5.0 (Multi-Container & Cache Mode)
|
||||||
|
-- 支持带缓存的复杂容器间传输
|
||||||
|
-- 配置保存在 config.cfg
|
||||||
|
local configFile = "config.cfg"
|
||||||
|
local tasks = {}
|
||||||
|
local settings = {
|
||||||
|
logEnabled = false, -- 日志开关,默认关闭
|
||||||
|
zhlog = false, -- 中文日志开关,默认关闭
|
||||||
|
delay = 0.01 -- 轮询延迟,默认0.01秒
|
||||||
|
}
|
||||||
|
local directions = {
|
||||||
|
up = "up", down = "down", forward = "forward"
|
||||||
|
}
|
||||||
|
local Tlist = {} -- 缓存传输列表
|
||||||
|
local lastOutput = {} -- 记录每个优先级组上次使用的输出容器
|
||||||
|
|
||||||
|
-- 加载配置
|
||||||
|
local function loadConfig()
|
||||||
|
if fs.exists(configFile) then
|
||||||
|
local file = fs.open(configFile, "r")
|
||||||
|
local data = file.readAll()
|
||||||
|
file.close()
|
||||||
|
local config = textutils.unserialize(data) or {}
|
||||||
|
|
||||||
|
-- 加载设置
|
||||||
|
if config.settings then
|
||||||
|
settings.logEnabled = config.settings.logEnabled or settings.logEnabled
|
||||||
|
settings.zhlog = config.settings.zhlog or settings.zhlog
|
||||||
|
settings.delay = config.settings.delay or settings.delay
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 加载任务
|
||||||
|
tasks = config.tasks or {}
|
||||||
|
|
||||||
|
-- 加载传输列表
|
||||||
|
Tlist = config.Tlist or {}
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
loadConfig()
|
||||||
|
|
||||||
|
-- 保存配置
|
||||||
|
local function saveConfig()
|
||||||
|
local config = {
|
||||||
|
settings = {
|
||||||
|
logEnabled = settings.logEnabled,
|
||||||
|
zhlog = settings.zhlog,
|
||||||
|
delay = settings.delay
|
||||||
|
},
|
||||||
|
tasks = tasks,
|
||||||
|
Tlist = Tlist
|
||||||
|
}
|
||||||
|
|
||||||
|
local file = fs.open(configFile, "w")
|
||||||
|
file.write(textutils.serialize(config))
|
||||||
|
file.close()
|
||||||
|
end
|
||||||
|
|
||||||
|
if settings.zhlog then
|
||||||
|
printUtf8 = load(http.get("https://alist.liulikeji.cn/d/HFS/utf8ptrint.lua").readAll())()
|
||||||
|
term.clear()
|
||||||
|
term.setCursorPos(1,1)
|
||||||
|
else
|
||||||
|
function printUtf8(logMessage,color,bcolor)
|
||||||
|
term.setTextColour(color)
|
||||||
|
term.setBackgroundColour(bcolor)
|
||||||
|
print(logMessage)
|
||||||
|
term.setTextColour(colors.white)
|
||||||
|
term.setBackgroundColour(colors.black)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 日志级别定义
|
||||||
|
local LOG_LEVEL = {
|
||||||
|
SUCCESS = {name = "SUCCESS", color = colors.green},
|
||||||
|
SKIPPED = {name = "SKIPPED", color = colors.orange},
|
||||||
|
INFO = {name = "INFO", color = colors.white},
|
||||||
|
WARNING = {name = "WARNING", color = colors.yellow},
|
||||||
|
ERROR = {name = "ERROR", color = colors.red},
|
||||||
|
DEBUG = {name = "DEBUG", color = colors.lightGray}
|
||||||
|
}
|
||||||
|
|
||||||
|
-- 日志记录函数
|
||||||
|
local function log(level, zhMessage, enMessage)
|
||||||
|
if level ~= LOG_LEVEL.ERROR then
|
||||||
|
if not settings.logEnabled then return end
|
||||||
|
end
|
||||||
|
|
||||||
|
local message = settings.zhlog and zhMessage or enMessage
|
||||||
|
local timeStr = os.date("%H:%M:%S")
|
||||||
|
local levelName = level.name
|
||||||
|
local color = level.color
|
||||||
|
|
||||||
|
-- 格式化日志消息
|
||||||
|
local logMessage = string.format("[%s] %s: %s", timeStr, levelName, message)
|
||||||
|
|
||||||
|
-- 打印日志
|
||||||
|
printUtf8(logMessage, color, colors.black)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 命令输出函数(双语支持)
|
||||||
|
local function echo(zhMessage, enMessage)
|
||||||
|
local message = settings.zhlog and zhMessage or enMessage
|
||||||
|
printUtf8(message, colors.lightBlue, colors.black)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 初始化日志
|
||||||
|
log(LOG_LEVEL.INFO, "程序开始 - 海龟物流系统 v5.0", "Program started - Turtle Logistics System v5.0")
|
||||||
|
|
||||||
|
-- 获取容器物品列表
|
||||||
|
local function getContainerItems(containerName)
|
||||||
|
local container = peripheral.wrap(containerName)
|
||||||
|
if not container then
|
||||||
|
log(LOG_LEVEL.ERROR, "错误: 容器不存在: "..containerName, "Error: Container not found: "..containerName)
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local items = {}
|
||||||
|
local containerList = container.list()
|
||||||
|
|
||||||
|
for slot, item in pairs(containerList) do
|
||||||
|
items[tostring(slot)] = {
|
||||||
|
count = item.count,
|
||||||
|
name = item.name
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
return items
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 比较两个物品列表是否相同
|
||||||
|
local function compareItems(items1, items2)
|
||||||
|
if not items1 or not items2 then return false end
|
||||||
|
|
||||||
|
-- 检查槽位数量
|
||||||
|
local count1 = 0
|
||||||
|
for _ in pairs(items1) do count1 = count1 + 1 end
|
||||||
|
local count2 = 0
|
||||||
|
for _ in pairs(items2) do count2 = count2 + 1 end
|
||||||
|
if count1 ~= count2 then return false end
|
||||||
|
|
||||||
|
-- 检查每个槽位
|
||||||
|
for slot, item1 in pairs(items1) do
|
||||||
|
local item2 = items2[slot]
|
||||||
|
if not item2 then return false end
|
||||||
|
if item1.name ~= item2.name or item1.count ~= item2.count then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 获取容器中不在Tlist中的物品
|
||||||
|
local function getNewItems(containerName)
|
||||||
|
local currentItems = getContainerItems(containerName)
|
||||||
|
if not currentItems then return nil end
|
||||||
|
|
||||||
|
local tlistItems = Tlist[containerName] or {}
|
||||||
|
local newItems = {}
|
||||||
|
|
||||||
|
for slot, item in pairs(currentItems) do
|
||||||
|
local tlistItem = tlistItems[slot]
|
||||||
|
if not tlistItem or tlistItem.name ~= item.name or tlistItem.count ~= item.count then
|
||||||
|
newItems[slot] = item
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return newItems
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 更新Tlist中的容器记录
|
||||||
|
local function updateTlist(containerName, items)
|
||||||
|
if not Tlist[containerName] then
|
||||||
|
Tlist[containerName] = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 只更新传入的物品槽位
|
||||||
|
for slot, item in pairs(items) do
|
||||||
|
Tlist[containerName][slot] = {
|
||||||
|
name = item.name,
|
||||||
|
count = item.count
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 清理Tlist中缺失的物品
|
||||||
|
local function cleanTlist(containerName)
|
||||||
|
if not Tlist[containerName] then return end
|
||||||
|
|
||||||
|
local currentItems = getContainerItems(containerName)
|
||||||
|
if not currentItems then return end
|
||||||
|
|
||||||
|
local toRemove = {}
|
||||||
|
|
||||||
|
-- 找出Tlist中存在但容器中不存在的物品
|
||||||
|
for slot, tlistItem in pairs(Tlist[containerName]) do
|
||||||
|
local currentItem = currentItems[slot]
|
||||||
|
if not currentItem or currentItem.name ~= tlistItem.name or currentItem.count ~= tlistItem.count then
|
||||||
|
table.insert(toRemove, slot)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 移除这些物品
|
||||||
|
for _, slot in ipairs(toRemove) do
|
||||||
|
Tlist[containerName][slot] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 辅助函数:检查物品是否通过过滤规则
|
||||||
|
local function isItemAllowed(itemName, containerConfig)
|
||||||
|
-- 如果有白名单,只允许白名单中的物品
|
||||||
|
if containerConfig.whitelist then
|
||||||
|
return containerConfig.whitelist[itemName] ~= nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 如果有黑名单,排除黑名单中的物品
|
||||||
|
if containerConfig.blacklist then
|
||||||
|
return containerConfig.blacklist[itemName] == nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 没有过滤规则时允许所有物品
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 获取物品的传输数量限制
|
||||||
|
local function getTransferLimit(itemName, containerConfig)
|
||||||
|
-- 检查白名单中是否有该物品的数量限制
|
||||||
|
if containerConfig.whitelist and containerConfig.whitelist[itemName] then
|
||||||
|
local countSetting = containerConfig.whitelist[itemName].count
|
||||||
|
if countSetting and countSetting > 0 then
|
||||||
|
return countSetting
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- 如果没有限制或限制为0,则返回nil表示传输全部
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 在 executeItemTask 函数中添加更多调试日志
|
||||||
|
local function executeItemTask(task)
|
||||||
|
log(LOG_LEVEL.INFO, "开始执行物品任务", "Starting item transfer task")
|
||||||
|
|
||||||
|
-- 收集所有输入容器的物品
|
||||||
|
local allItemsToTransfer = {}
|
||||||
|
|
||||||
|
-- 处理输入容器
|
||||||
|
for inputName, inputConfig in pairs(task.input) do
|
||||||
|
log(LOG_LEVEL.DEBUG, "开始处理输入容器: "..inputName, "Starting processing input container: "..inputName)
|
||||||
|
|
||||||
|
local container = peripheral.wrap(inputName)
|
||||||
|
if not container then
|
||||||
|
log(LOG_LEVEL.ERROR, "错误: 输入容器不存在: "..inputName, "Error: Input container not found: "..inputName)
|
||||||
|
goto continue
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 缓存容器处理
|
||||||
|
if inputConfig.cache then
|
||||||
|
log(LOG_LEVEL.DEBUG, "容器 "..inputName.." 是缓存容器", "Container "..inputName.." is a cache container")
|
||||||
|
|
||||||
|
-- 清理Tlist中缺失的物品
|
||||||
|
cleanTlist(inputName)
|
||||||
|
|
||||||
|
-- 获取当前物品状态
|
||||||
|
local currentItems = getContainerItems(inputName)
|
||||||
|
if not currentItems then
|
||||||
|
log(LOG_LEVEL.DEBUG, "无法获取容器物品: "..inputName, "Failed to get container items: "..inputName)
|
||||||
|
goto continue
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 检查是否与Tlist完全相同
|
||||||
|
if compareItems(currentItems, Tlist[inputName] or {}) then
|
||||||
|
log(LOG_LEVEL.DEBUG, "容器内容与Tlist相同,跳过: "..inputName, "Container contents match Tlist, skipping: "..inputName)
|
||||||
|
goto continue
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 获取新物品(不在Tlist中的)
|
||||||
|
local newItems = getNewItems(inputName)
|
||||||
|
if not newItems or not next(newItems) then
|
||||||
|
log(LOG_LEVEL.DEBUG, "没有新物品,跳过: "..inputName, "No new items, skipping: "..inputName)
|
||||||
|
goto continue
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 添加到传输列表(应用输入容器过滤)
|
||||||
|
for slot, item in pairs(newItems) do
|
||||||
|
if isItemAllowed(item.name, inputConfig) then
|
||||||
|
-- 获取传输数量限制
|
||||||
|
local transferLimit = getTransferLimit(item.name, inputConfig)
|
||||||
|
|
||||||
|
-- 检查是否满足传输条件
|
||||||
|
if transferLimit and item.count < transferLimit then
|
||||||
|
log(LOG_LEVEL.DEBUG, "物品 "..item.name.." 数量不足("..item.count.."<"..transferLimit.."),跳过传输",
|
||||||
|
"Item "..item.name.." insufficient quantity ("..item.count.."<"..transferLimit.."), skipping transfer")
|
||||||
|
goto continue_item
|
||||||
|
end
|
||||||
|
|
||||||
|
local transferCount = transferLimit or item.count
|
||||||
|
|
||||||
|
-- 如果数量大于0才添加
|
||||||
|
if transferCount > 0 then
|
||||||
|
log(LOG_LEVEL.DEBUG, "添加物品到传输列表: "..item.name.." x"..transferCount.." (槽位 "..slot..")",
|
||||||
|
"Adding item to transfer list: "..item.name.." x"..transferCount.." (slot "..slot..")")
|
||||||
|
table.insert(allItemsToTransfer, {
|
||||||
|
container = inputName,
|
||||||
|
slot = tonumber(slot),
|
||||||
|
name = item.name,
|
||||||
|
count = transferCount
|
||||||
|
})
|
||||||
|
else
|
||||||
|
log(LOG_LEVEL.DEBUG, "物品 "..item.name.." 数量不足,跳过传输",
|
||||||
|
"Item "..item.name.." insufficient quantity, skipping transfer")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
log(LOG_LEVEL.DEBUG, "物品 "..item.name.." 被输入容器 "..inputName.." 过滤",
|
||||||
|
"Item "..item.name.." filtered by input container "..inputName)
|
||||||
|
end
|
||||||
|
|
||||||
|
::continue_item::
|
||||||
|
end
|
||||||
|
else
|
||||||
|
log(LOG_LEVEL.DEBUG, "容器 "..inputName.." 不是缓存容器", "Container "..inputName.." is not a cache container")
|
||||||
|
local items = getContainerItems(inputName)
|
||||||
|
if not items then
|
||||||
|
log(LOG_LEVEL.DEBUG, "无法获取容器物品: "..inputName, "Failed to get container items: "..inputName)
|
||||||
|
goto continue
|
||||||
|
end
|
||||||
|
|
||||||
|
for slot, item in pairs(items) do
|
||||||
|
-- 应用输入容器过滤
|
||||||
|
if isItemAllowed(item.name, inputConfig) then
|
||||||
|
-- 获取传输数量限制
|
||||||
|
local transferLimit = getTransferLimit(item.name, inputConfig)
|
||||||
|
|
||||||
|
-- 检查是否满足传输条件
|
||||||
|
if transferLimit and item.count < transferLimit then
|
||||||
|
log(LOG_LEVEL.DEBUG, "物品 "..item.name.." 数量不足("..item.count.."<"..transferLimit.."),跳过传输",
|
||||||
|
"Item "..item.name.." insufficient quantity ("..item.count.."<"..transferLimit.."), skipping transfer")
|
||||||
|
goto continue_item_non_cache
|
||||||
|
end
|
||||||
|
|
||||||
|
local transferCount = transferLimit or item.count
|
||||||
|
|
||||||
|
-- 如果数量大于0才添加
|
||||||
|
if transferCount > 0 then
|
||||||
|
log(LOG_LEVEL.DEBUG, "添加物品到传输列表: "..item.name.." x"..transferCount.." (槽位 "..slot..")",
|
||||||
|
"Adding item to transfer list: "..item.name.." x"..transferCount.." (slot "..slot..")")
|
||||||
|
table.insert(allItemsToTransfer, {
|
||||||
|
container = inputName,
|
||||||
|
slot = tonumber(slot),
|
||||||
|
name = item.name,
|
||||||
|
count = transferCount
|
||||||
|
})
|
||||||
|
else
|
||||||
|
log(LOG_LEVEL.DEBUG, "物品 "..item.name.." 数量不足,跳过传输",
|
||||||
|
"Item "..item.name.." insufficient quantity, skipping transfer")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
log(LOG_LEVEL.DEBUG, "物品 "..item.name.." 被输入容器 "..inputName.." 过滤",
|
||||||
|
"Item "..item.name.." filtered by input container "..inputName)
|
||||||
|
end
|
||||||
|
|
||||||
|
::continue_item_non_cache::
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
::continue::
|
||||||
|
end
|
||||||
|
|
||||||
|
if #allItemsToTransfer == 0 then
|
||||||
|
log(LOG_LEVEL.SKIPPED, "没有物品需要传输", "No items to transfer")
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
log(LOG_LEVEL.INFO, "找到 "..#allItemsToTransfer.." 个物品需要传输", "Found "..#allItemsToTransfer.." items to transfer")
|
||||||
|
|
||||||
|
-- 按优先级分组输出容器
|
||||||
|
local outputGroups = {}
|
||||||
|
for outputName, outputConfig in pairs(task.output) do
|
||||||
|
local priority = outputConfig.priority or 1
|
||||||
|
if not outputGroups[priority] then
|
||||||
|
outputGroups[priority] = {}
|
||||||
|
end
|
||||||
|
table.insert(outputGroups[priority], {
|
||||||
|
name = outputName,
|
||||||
|
config = outputConfig
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 按优先级排序(从高到低)
|
||||||
|
local sortedPriorities = {}
|
||||||
|
for priority in pairs(outputGroups) do
|
||||||
|
table.insert(sortedPriorities, priority)
|
||||||
|
end
|
||||||
|
table.sort(sortedPriorities, function(a, b) return a > b end)
|
||||||
|
|
||||||
|
-- 传输物品
|
||||||
|
local transferred = false
|
||||||
|
|
||||||
|
for _, itemData in ipairs(allItemsToTransfer) do
|
||||||
|
log(LOG_LEVEL.DEBUG, "尝试传输物品: "..itemData.name.." x"..itemData.count.." (源容器: "..itemData.container..", 槽位: "..itemData.slot..")",
|
||||||
|
"Attempting to transfer item: "..itemData.name.." x"..itemData.count.." (source: "..itemData.container..", slot: "..itemData.slot..")")
|
||||||
|
|
||||||
|
local sourceContainer = peripheral.wrap(itemData.container)
|
||||||
|
if not sourceContainer then
|
||||||
|
log(LOG_LEVEL.ERROR, "错误: 源容器不存在: "..itemData.container, "Error: Source container not found: "..itemData.container)
|
||||||
|
goto next_item
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 尝试推送到输出容器
|
||||||
|
local pushed = false
|
||||||
|
|
||||||
|
-- 按优先级尝试输出
|
||||||
|
for _, priority in ipairs(sortedPriorities) do
|
||||||
|
local group = outputGroups[priority]
|
||||||
|
|
||||||
|
-- 轮询输出
|
||||||
|
local startIdx = lastOutput[priority] or 1
|
||||||
|
local idx = startIdx
|
||||||
|
local attempts = 0
|
||||||
|
|
||||||
|
repeat
|
||||||
|
local output = group[idx]
|
||||||
|
log(LOG_LEVEL.DEBUG, "尝试输出容器: "..output.name.." (优先级 "..priority..", 尝试 "..attempts.."/"..#group..")",
|
||||||
|
"Attempting output container: "..output.name.." (priority "..priority..", attempt "..attempts.."/"..#group..")")
|
||||||
|
|
||||||
|
local outputContainer = peripheral.wrap(output.name)
|
||||||
|
|
||||||
|
if outputContainer then
|
||||||
|
-- 应用输出容器过滤
|
||||||
|
if not isItemAllowed(itemData.name, output.config) then
|
||||||
|
log(LOG_LEVEL.DEBUG, "物品 "..itemData.name.." 被输出容器 "..output.name.." 过滤",
|
||||||
|
"Item "..itemData.name.." filtered by output container "..output.name)
|
||||||
|
goto next_output
|
||||||
|
end
|
||||||
|
|
||||||
|
log(LOG_LEVEL.DEBUG, "尝试推送到 "..output.name.." (优先级 "..priority..")", "Attempting push to "..output.name.." (priority "..priority..")")
|
||||||
|
|
||||||
|
-- 推送物品
|
||||||
|
local pushedCount = sourceContainer.pushItems(
|
||||||
|
output.name,
|
||||||
|
itemData.slot,
|
||||||
|
itemData.count
|
||||||
|
)
|
||||||
|
|
||||||
|
if pushedCount > 0 then
|
||||||
|
log(LOG_LEVEL.SUCCESS, "成功推送 "..pushedCount.." 个 "..itemData.name.." 到 "..output.name, "Successfully pushed "..pushedCount.." "..itemData.name.." to "..output.name)
|
||||||
|
pushed = true
|
||||||
|
transferred = true
|
||||||
|
|
||||||
|
-- 更新输出容器的Tlist(如果是缓存容器)
|
||||||
|
if output.config.cache then
|
||||||
|
log(LOG_LEVEL.DEBUG, "更新输出容器Tlist: "..output.name, "Updating output container Tlist: "..output.name)
|
||||||
|
if not Tlist[output.name] then
|
||||||
|
Tlist[output.name] = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 获取输出容器当前物品
|
||||||
|
local outputItems = getContainerItems(output.name)
|
||||||
|
if outputItems then
|
||||||
|
-- 只更新传输的物品槽位
|
||||||
|
for slot, item in pairs(outputItems) do
|
||||||
|
if item.name == itemData.name then
|
||||||
|
Tlist[output.name][slot] = {
|
||||||
|
name = item.name,
|
||||||
|
count = item.count
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 更新输入容器的Tlist(如果是缓存容器)
|
||||||
|
if task.input[itemData.container] and task.input[itemData.container].cache then
|
||||||
|
log(LOG_LEVEL.DEBUG, "更新输入容器Tlist: "..itemData.container, "Updating input container Tlist: "..itemData.container)
|
||||||
|
updateTlist(itemData.container, {
|
||||||
|
[tostring(itemData.slot)] = {
|
||||||
|
name = itemData.name,
|
||||||
|
count = itemData.count - pushedCount
|
||||||
|
}
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 更新轮询索引
|
||||||
|
lastOutput[priority] = idx % #group + 1
|
||||||
|
break
|
||||||
|
else
|
||||||
|
log(LOG_LEVEL.WARNING, "推送失败到 "..output.name.." (可能容器已满或无法接收)",
|
||||||
|
"Push failed to "..output.name.." (container may be full or unable to receive)")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
log(LOG_LEVEL.ERROR, "输出容器不存在或无法访问: "..output.name, "Output container not found or inaccessible: "..output.name)
|
||||||
|
end
|
||||||
|
|
||||||
|
::next_output::
|
||||||
|
idx = idx % #group + 1
|
||||||
|
attempts = attempts + 1
|
||||||
|
until attempts >= #group
|
||||||
|
|
||||||
|
if pushed then break end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not pushed then
|
||||||
|
log(LOG_LEVEL.WARNING, "警告: 无法传输物品 "..itemData.name.." x"..itemData.count.." (所有输出容器尝试失败)",
|
||||||
|
"Warning: Failed to transfer item "..itemData.name.." x"..itemData.count.." (all output containers failed)")
|
||||||
|
end
|
||||||
|
|
||||||
|
::next_item::
|
||||||
|
end
|
||||||
|
|
||||||
|
if not transferred then
|
||||||
|
log(LOG_LEVEL.DEBUG, "所有物品传输尝试失败", "All item transfer attempts failed")
|
||||||
|
end
|
||||||
|
|
||||||
|
return transferred
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 执行流体传输任务
|
||||||
|
local function executeFluidTask(task)
|
||||||
|
log(LOG_LEVEL.INFO, "开始执行流体任务", "Starting fluid transfer task")
|
||||||
|
|
||||||
|
local transferred = false
|
||||||
|
|
||||||
|
-- 处理输入容器
|
||||||
|
for inputName, inputConfig in pairs(task.input) do
|
||||||
|
log(LOG_LEVEL.INFO, "处理输入容器: "..inputName, "Processing input container: "..inputName)
|
||||||
|
|
||||||
|
local source = peripheral.wrap(inputName)
|
||||||
|
if not source then
|
||||||
|
log(LOG_LEVEL.ERROR, "错误: 源容器不存在: "..inputName, "Error: Source container not found: "..inputName)
|
||||||
|
goto continue
|
||||||
|
end
|
||||||
|
|
||||||
|
if not source.tanks then
|
||||||
|
log(LOG_LEVEL.WARNING, "警告: 容器不支持流体: "..inputName.." - 跳过", "Warning: Container does not support fluids: "..inputName.." - skipping")
|
||||||
|
goto continue
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 获取流体槽 - 使用 pairs 而不是 # 来计算数量
|
||||||
|
local tanks = source.tanks()
|
||||||
|
if not tanks then
|
||||||
|
log(LOG_LEVEL.INFO, "容器中没有流体槽: "..inputName, "No fluid tanks in container: "..inputName)
|
||||||
|
goto continue
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 正确计算流体槽数量
|
||||||
|
local tankCount = 0
|
||||||
|
for _ in pairs(tanks) do tankCount = tankCount + 1 end
|
||||||
|
|
||||||
|
if tankCount == 0 then
|
||||||
|
log(LOG_LEVEL.INFO, "容器中没有流体槽: "..inputName, "No fluid tanks in container: "..inputName)
|
||||||
|
goto continue
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 使用 pairs 遍历所有流体槽
|
||||||
|
for tankIndex, tank in pairs(tanks) do
|
||||||
|
-- 确保 tank 是有效的表
|
||||||
|
if type(tank) == "table" and tank.name and tank.amount then
|
||||||
|
-- 应用输入容器过滤
|
||||||
|
if tank.amount > 0 and isItemAllowed(tank.name, inputConfig) then
|
||||||
|
log(LOG_LEVEL.INFO, "找到可传输流体: "..tank.name.." ("..tank.amount.."mB)", "Found transferable fluid: "..tank.name.." ("..tank.amount.."mB)")
|
||||||
|
|
||||||
|
-- 按优先级分组输出容器
|
||||||
|
local outputGroups = {}
|
||||||
|
for outputName, outputConfig in pairs(task.output) do
|
||||||
|
local priority = outputConfig.priority or 1
|
||||||
|
if not outputGroups[priority] then
|
||||||
|
outputGroups[priority] = {}
|
||||||
|
end
|
||||||
|
table.insert(outputGroups[priority], {
|
||||||
|
name = outputName,
|
||||||
|
config = outputConfig
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 按优先级排序(从高到低)
|
||||||
|
local sortedPriorities = {}
|
||||||
|
for priority in pairs(outputGroups) do
|
||||||
|
table.insert(sortedPriorities, priority)
|
||||||
|
end
|
||||||
|
table.sort(sortedPriorities, function(a, b) return a > b end)
|
||||||
|
|
||||||
|
-- 尝试传输流体
|
||||||
|
local pushed = false
|
||||||
|
local amountToTransfer = tank.amount
|
||||||
|
|
||||||
|
for _, priority in ipairs(sortedPriorities) do
|
||||||
|
local group = outputGroups[priority]
|
||||||
|
|
||||||
|
-- 轮询输出
|
||||||
|
local startIdx = lastOutput[priority] or 1
|
||||||
|
local idx = startIdx
|
||||||
|
local attempts = 0
|
||||||
|
|
||||||
|
repeat
|
||||||
|
local output = group[idx]
|
||||||
|
local destination = peripheral.wrap(output.name)
|
||||||
|
|
||||||
|
if destination and destination.tanks then
|
||||||
|
-- 应用输出容器过滤
|
||||||
|
if not isItemAllowed(tank.name, output.config) then
|
||||||
|
log(LOG_LEVEL.INFO, "流体 "..tank.name.." 被输出容器 "..output.name.." 过滤",
|
||||||
|
"Fluid "..tank.name.." filtered by output container "..output.name)
|
||||||
|
goto next_output
|
||||||
|
end
|
||||||
|
|
||||||
|
log(LOG_LEVEL.INFO, "尝试推送到 "..output.name.." (优先级 "..priority..")", "Attempting push to "..output.name.." (priority "..priority..")")
|
||||||
|
|
||||||
|
-- 推送流体
|
||||||
|
local pushedAmount = source.pushFluid(
|
||||||
|
output.name,
|
||||||
|
amountToTransfer,
|
||||||
|
tank.name,
|
||||||
|
tankIndex
|
||||||
|
)
|
||||||
|
|
||||||
|
if pushedAmount > 0 then
|
||||||
|
log(LOG_LEVEL.SUCCESS, "成功推送 "..pushedAmount.."mB "..tank.name.." 到 "..output.name, "Successfully pushed "..pushedAmount.."mB "..tank.name.." to "..output.name)
|
||||||
|
pushed = true
|
||||||
|
transferred = true
|
||||||
|
amountToTransfer = amountToTransfer - pushedAmount
|
||||||
|
|
||||||
|
-- 更新输出容器的Tlist(如果是缓存容器)
|
||||||
|
if output.config.cache then
|
||||||
|
log(LOG_LEVEL.DEBUG, "更新输出容器Tlist: "..output.name, "Updating output container Tlist: "..output.name)
|
||||||
|
if not Tlist[output.name] then
|
||||||
|
Tlist[output.name] = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 获取输出容器当前流体
|
||||||
|
local destTanks = destination.tanks()
|
||||||
|
if destTanks then
|
||||||
|
for _, destTank in pairs(destTanks) do
|
||||||
|
if destTank.name == tank.name then
|
||||||
|
Tlist[output.name][tank.name] = {
|
||||||
|
amount = destTank.amount
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 更新轮询索引
|
||||||
|
lastOutput[priority] = idx % #group + 1
|
||||||
|
else
|
||||||
|
log(LOG_LEVEL.WARNING, "推送失败到 "..output.name, "Push failed to "..output.name)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if not destination then
|
||||||
|
log(LOG_LEVEL.ERROR, "错误: 输出容器不存在: "..output.name, "Error: Output container not found: "..output.name)
|
||||||
|
else
|
||||||
|
log(LOG_LEVEL.WARNING, "错误: 输出容器不支持流体: "..output.name, "Error: Output container does not support fluids: "..output.name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
::next_output::
|
||||||
|
idx = idx % #group + 1
|
||||||
|
attempts = attempts + 1
|
||||||
|
until attempts >= #group or amountToTransfer <= 0
|
||||||
|
|
||||||
|
if amountToTransfer <= 0 then break end
|
||||||
|
end
|
||||||
|
|
||||||
|
if amountToTransfer > 0 then
|
||||||
|
log(LOG_LEVEL.WARNING, string.format("警告: 仍有 %dmB %s 未传输", amountToTransfer, tank.name),
|
||||||
|
string.format("Warning: Still %dmB %s not transferred", amountToTransfer, tank.name))
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if tank.amount == 0 then
|
||||||
|
log(LOG_LEVEL.INFO, "槽位 "..tankIndex.." 无流体", "Tank "..tankIndex.." has no fluid")
|
||||||
|
else
|
||||||
|
log(LOG_LEVEL.INFO, "流体 "..tank.name.." 被输入容器 "..inputName.." 过滤",
|
||||||
|
"Fluid "..tank.name.." filtered by input container "..inputName)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
log(LOG_LEVEL.WARNING, "警告: 槽位 "..tankIndex.." 的数据无效", "Warning: Invalid data for tank "..tankIndex)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
::continue::
|
||||||
|
end
|
||||||
|
|
||||||
|
return transferred
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 执行任务
|
||||||
|
local function executeTask(task)
|
||||||
|
if task.type == "item" then
|
||||||
|
return executeItemTask(task)
|
||||||
|
elseif task.type == "fluid" then
|
||||||
|
return executeFluidTask(task)
|
||||||
|
else
|
||||||
|
log(LOG_LEVEL.ERROR, "错误: 未知任务类型: "..(task.type or "nil"), "Error: Unknown task type: "..(task.type or "nil"))
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 主任务循环
|
||||||
|
local function taskLoop()
|
||||||
|
log(LOG_LEVEL.INFO, "任务循环开始", "Task loop started")
|
||||||
|
while true do
|
||||||
|
for i, task in ipairs(tasks) do
|
||||||
|
log(LOG_LEVEL.INFO, "开始任务 #"..i, "Starting task #"..i)
|
||||||
|
local success, result = pcall(executeTask, task)
|
||||||
|
if not success then
|
||||||
|
log(LOG_LEVEL.ERROR, "任务 #"..i.." 错误: "..tostring(result), "Task #"..i.." error: "..tostring(result))
|
||||||
|
elseif result then
|
||||||
|
log(LOG_LEVEL.SUCCESS, "任务 #"..i.." 成功执行", "Task #"..i.." executed successfully")
|
||||||
|
else
|
||||||
|
log(LOG_LEVEL.SKIPPED, "任务 #"..i.." 未执行传输", "Task #"..i.." no transfer performed")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
os.sleep(settings.delay)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 命令处理
|
||||||
|
local function processCommand(cmd)
|
||||||
|
local args = {}
|
||||||
|
for word in cmd:gmatch("%S+") do
|
||||||
|
table.insert(args, word)
|
||||||
|
end
|
||||||
|
|
||||||
|
if #args == 0 then return end
|
||||||
|
|
||||||
|
local command = args[1]:lower()
|
||||||
|
|
||||||
|
if command == "add" and #args >= 2 then
|
||||||
|
-- 合并参数为JSON字符串
|
||||||
|
local jsonStr = table.concat(args, " ", 2)
|
||||||
|
local success, task = pcall(textutils.unserializeJSON, jsonStr)
|
||||||
|
|
||||||
|
if success and type(task) == "table" then
|
||||||
|
-- 验证任务类型
|
||||||
|
if not task.type then
|
||||||
|
echo("错误: 任务缺少类型字段", "Error: Task missing type field")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if task.type ~= "item" and task.type ~= "fluid" then
|
||||||
|
echo("错误: 无效的任务类型: "..task.type, "Error: Invalid task type: "..task.type)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 验证输入输出
|
||||||
|
if not task.input or not task.output then
|
||||||
|
echo("错误: 任务缺少输入或输出字段", "Error: Task missing input or output fields")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(tasks, task)
|
||||||
|
saveConfig()
|
||||||
|
echo("任务添加成功", "Task added successfully")
|
||||||
|
else
|
||||||
|
echo("错误: 无效的JSON格式", "Error: Invalid JSON format")
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif command == "list" then
|
||||||
|
if #tasks == 0 then
|
||||||
|
echo("没有配置任务", "No tasks configured")
|
||||||
|
else
|
||||||
|
for i, task in ipairs(tasks) do
|
||||||
|
echo("任务 #"..i.." ("..task.type..")", "Task #"..i.." ("..task.type..")")
|
||||||
|
echo(" 输入容器:", " Input containers:")
|
||||||
|
for name, config in pairs(task.input) do
|
||||||
|
local cacheStr = config.cache and "缓存" or "非缓存"
|
||||||
|
local cacheStrEn = config.cache and "cache" or "non-cache"
|
||||||
|
|
||||||
|
-- 显示过滤规则
|
||||||
|
if config.whitelist then
|
||||||
|
local items = {}
|
||||||
|
for itemName, itemConfig in pairs(config.whitelist) do
|
||||||
|
if itemConfig.count and itemConfig.count > 0 then
|
||||||
|
table.insert(items, itemName.." (count="..itemConfig.count..")")
|
||||||
|
else
|
||||||
|
table.insert(items, itemName)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
echo(" "..name.." ("..cacheStr..", 白名单: "..table.concat(items, ", ")..")",
|
||||||
|
" "..name.." ("..cacheStrEn..", whitelist: "..table.concat(items, ", ")..")")
|
||||||
|
elseif config.blacklist then
|
||||||
|
local items = {}
|
||||||
|
for itemName in pairs(config.blacklist) do
|
||||||
|
table.insert(items, itemName)
|
||||||
|
end
|
||||||
|
echo(" 极"..name.." ("..cacheStr..", 黑名单: "..table.concat(items, ", ")..")",
|
||||||
|
" "..name.." ("..cacheStrEn..", blacklist: "..table.concat(items, ", ")..")")
|
||||||
|
else
|
||||||
|
echo(" "..name.." ("..cacheStr..")", " "..name.." ("..cacheStrEn..")")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
echo(" 输出容器:", " Output containers:")
|
||||||
|
for name, config in pairs(task.output) do
|
||||||
|
local cacheStr = config.cache and "缓存" or "非缓存"
|
||||||
|
local cacheStrEn = config.cache and "cache" or "non-cache"
|
||||||
|
local priority = config.priority or 1
|
||||||
|
|
||||||
|
-- 显示过滤规则
|
||||||
|
if config.whitelist then
|
||||||
|
local items = {}
|
||||||
|
for itemName, itemConfig in pairs(config.whitelist) do
|
||||||
|
if itemConfig.count and itemConfig.count > 0 then
|
||||||
|
table.insert(items, itemName.." (count="..itemConfig.count..")")
|
||||||
|
else
|
||||||
|
table.insert(items, itemName)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
echo(" "..name.." (优先级:"..priority..", "..cacheStr..", 白名单: "..table.concat(items, ", ")..")",
|
||||||
|
" "..name.." (priority:"..priority..", "..cacheStrEn..", whitelist: "..table.concat(items, ", ")..")")
|
||||||
|
elseif config.blacklist then
|
||||||
|
local items = {}
|
||||||
|
for itemName in pairs(config.blacklist) do
|
||||||
|
table.insert(items, itemName)
|
||||||
|
end
|
||||||
|
echo(" "..name.." (优先级:"..priority..", "..cacheStr..", 黑名单: "..table.concat(items, ", ")..")",
|
||||||
|
" "..name.." (priority:"..priority..", "..cacheStrEn..", blacklist: "..table.concat(items, ", ")..")")
|
||||||
|
else
|
||||||
|
echo(" "..name.." (优先级:"..priority..", "..cacheStr..")",
|
||||||
|
" "..name.." (priority:"..priority..", "..cacheStrEn..")")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif command == "remove" and #args >= 2 then
|
||||||
|
local index = tonumber(args[2])
|
||||||
|
if index and tasks[index] then
|
||||||
|
table.remove(tasks, index)
|
||||||
|
saveConfig()
|
||||||
|
echo("任务 "..index.." 已移除", "Task "..index.." removed")
|
||||||
|
else
|
||||||
|
echo("无效的任务索引", "Invalid task index")
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif command == "log" then
|
||||||
|
if #args >= 2 then
|
||||||
|
if args[2]:lower() == "on" then
|
||||||
|
settings.logEnabled = true
|
||||||
|
saveConfig()
|
||||||
|
echo("日志已启用", "Logging enabled")
|
||||||
|
elseif args[2]:lower() == "off" then
|
||||||
|
settings.logEnabled = false
|
||||||
|
saveConfig()
|
||||||
|
echo("日志已禁用", "Logging disabled")
|
||||||
|
else
|
||||||
|
echo("无效参数,使用 'log on' 或 'log off'", "Invalid argument, use 'log on' or 'log off'")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
echo("当前日志状态: "..(settings.logEnabled and "启用" or "禁用"),
|
||||||
|
"Current log status: "..(settings.logEnabled and "enabled" or "disabled"))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 添加 set 命令
|
||||||
|
elseif command == "set" and #args >= 3 then
|
||||||
|
local key = args[2]:lower()
|
||||||
|
local value = args[3]
|
||||||
|
|
||||||
|
if key == "log" then
|
||||||
|
if value == "true" then
|
||||||
|
settings.logEnabled = true
|
||||||
|
saveConfig()
|
||||||
|
echo("日志已启用", "Logging enabled")
|
||||||
|
elseif value == "false" then
|
||||||
|
settings.logEnabled = false
|
||||||
|
saveConfig()
|
||||||
|
echo("日志已禁用", "Logging disabled")
|
||||||
|
else
|
||||||
|
echo("无效值,使用 'true' 或 'false'", "Invalid value, use 'true' or 'false'")
|
||||||
|
end
|
||||||
|
elseif key == "zh" then
|
||||||
|
if value == "true" then
|
||||||
|
settings.zhlog = true
|
||||||
|
saveConfig()
|
||||||
|
echo("中文日志已启用", "Chinese log enabled")
|
||||||
|
elseif value == "false" then
|
||||||
|
settings.zhlog = false
|
||||||
|
saveConfig()
|
||||||
|
echo("中文日志已禁用", "Chinese log disabled")
|
||||||
|
else
|
||||||
|
echo("无效值,使用 'true' 或 'false'", "Invalid value, use 'true' or 'false'")
|
||||||
|
end
|
||||||
|
elseif key == "delay" then
|
||||||
|
local numValue = tonumber(value)
|
||||||
|
if numValue and numValue > 0 then
|
||||||
|
settings.delay = numValue
|
||||||
|
saveConfig()
|
||||||
|
echo("轮询延迟已设置为: "..numValue.." 秒", "Polling delay set to: "..numValue.." seconds")
|
||||||
|
else
|
||||||
|
echo("无效值,请输入大于0的数字", "Invalid value, please enter a number greater than 0")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
echo("无效设置项,可用选项: logEnabled, zhlog, delay", "Invalid setting, available options: logEnabled, zhlog, delay")
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif command == "help" then
|
||||||
|
echo("可用命令:", "Available commands:")
|
||||||
|
echo("add <JSON任务> - 添加新任务", "add <JSON task> - Add new task")
|
||||||
|
echo(" 示例: add {\"type\":\"item\",\"input\":{\"left\":{\"cache\":true,\"whitelist\":{\"minecraft:stone\":{\"count\":64}}}},\"output\":{\"right\":{\"priority\":1,\"blacklist\":{\"minecraft:dirt\":{}}}}",
|
||||||
|
" Example: add {\"type\":\"item\",\"input\":{\"left\":{\"cache\":true,\"whitelist\":{\"minecraft:stone\":{\"count\":64}}}},\"output\":{\"right\":{\"priority\":1,\"blacklist\":{\"minecraft:dirt\":{}}}}")
|
||||||
|
echo("list - 列出所有任务", "list - List all tasks")
|
||||||
|
echo("remove <索引> - 移除任务", "remove <index> - Remove task")
|
||||||
|
echo("log [on|off] - 启用/禁用日志", "log [on|off] - Enable/disable logging")
|
||||||
|
echo("set <key> <value> - 修改设置", "set <key> <value> - Modify settings")
|
||||||
|
echo(" 可用键: logEnabled (true/false), zhlog (true/false), delay (数字)", " Available keys: logEnabled (true/false), zhlog (true/false), delay (number)")
|
||||||
|
echo("exit - 退出程序", "exit - Exit program")
|
||||||
|
|
||||||
|
elseif command == "exit" then
|
||||||
|
echo("正在退出程序", "Exiting program")
|
||||||
|
os.sleep(0.5)
|
||||||
|
error("程序终止", 0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 初始化
|
||||||
|
|
||||||
|
echo("海龟物流系统 v5.0 已启动。输入 'help' 查看命令。",
|
||||||
|
"Turtle Logistics System v5.0 started. Type 'help' for commands.")
|
||||||
|
|
||||||
|
-- 主循环
|
||||||
|
parallel.waitForAll(
|
||||||
|
function()
|
||||||
|
while true do
|
||||||
|
write("> ")
|
||||||
|
local command = read()
|
||||||
|
processCommand(command)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
taskLoop
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{"type":"item","input":{"create:basin_6":{"cache":true,"whitelist":{"minecraft:iron_ingot":{}}}},"output":{"create:basin_7":{"priority":1,"whitelist":{"minecraft:iron_ingot":{"count":10}}}}}
|
||||||
|
|
||||||
|
我在输出容器的白名单定理了数量,但程序不遵循数量参数而是直接传输了所有
|
||||||
13
Logistics/V5/web/index.html
Normal file
13
Logistics/V5/web/index.html
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html data-theme="emerald">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>web</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="module" src="/src/main.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
2193
Logistics/V5/web/package-lock.json
generated
Normal file
2193
Logistics/V5/web/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
22
Logistics/V5/web/package.json
Normal file
22
Logistics/V5/web/package.json
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"name": "web",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vite build",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@tailwindcss/vite": "^4.1.14",
|
||||||
|
"axios": "^1.12.2",
|
||||||
|
"daisyui": "^5.3.7",
|
||||||
|
"tailwindcss": "^4.1.14",
|
||||||
|
"vue": "^3.5.22"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vitejs/plugin-vue": "^6.0.1",
|
||||||
|
"vite": "^7.1.7"
|
||||||
|
}
|
||||||
|
}
|
||||||
1
Logistics/V5/web/public/vite.svg
Normal file
1
Logistics/V5/web/public/vite.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
||||||
|
After Width: | Height: | Size: 1.5 KiB |
548
Logistics/V5/web/src/App.vue
Normal file
548
Logistics/V5/web/src/App.vue
Normal file
@@ -0,0 +1,548 @@
|
|||||||
|
<template>
|
||||||
|
<div id="app" class="container mx-auto px-4 py-8">
|
||||||
|
<div class="text-center mb-8">
|
||||||
|
<h1 class="text-3xl font-bold text-gray-800">物流系统 v5.0</h1>
|
||||||
|
<p class="text-gray-600">任务配置创建工具</p>
|
||||||
|
|
||||||
|
<!-- 新增:项目信息和下载区域 -->
|
||||||
|
<div class="bg-blue-50 border border-blue-200 rounded-lg p-4 mt-4 max-w-2xl mx-auto">
|
||||||
|
<div class="flex flex-col sm:flex-row justify-between items-center gap-4">
|
||||||
|
<div class="text-left w-30">
|
||||||
|
<h3 class="font-semibold text-blue-800 mb-1">项目信息</h3>
|
||||||
|
<a href="http://git.liulikeji.cn/xingluo/CCTweaked-utils/src/branch/main/Logistics/V5"
|
||||||
|
target="_blank"
|
||||||
|
class="text-blue-600 hover:text-blue-800 underline text-sm">
|
||||||
|
📁项目仓库
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<code class="bg-gray-800 text-green-200 px-3 py-1 rounded text-sm">wget http://git.liulikeji.cn/xingluo/CCTweaked-utils/raw/branch/main/Logistics/V5/startup.lua</code>
|
||||||
|
<button @click="copyDownloadCommand" class="btn btn-sm btn-primary">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3" />
|
||||||
|
</svg>
|
||||||
|
复制
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bg-white rounded-xl shadow-lg p-6 mb-8">
|
||||||
|
<div class="flex flex-col md:flex-row gap-6">
|
||||||
|
<!-- 左侧配置区域 -->
|
||||||
|
<div class="w-full md:w-2/3">
|
||||||
|
<div class="mb-6">
|
||||||
|
<h2 class="text-xl font-semibold text-gray-800 mb-4">创建新任务</h2>
|
||||||
|
|
||||||
|
<!-- 传输类型选择 -->
|
||||||
|
<div class="mb-6">
|
||||||
|
<label class="block text-gray-700 font-medium mb-2">传输类型</label>
|
||||||
|
<div class="flex gap-4">
|
||||||
|
<label class="flex items-center">
|
||||||
|
<input type="radio" v-model="task.type" value="item" class="radio radio-primary mr-2">
|
||||||
|
<span>物品传输</span>
|
||||||
|
</label>
|
||||||
|
<label class="flex items-center">
|
||||||
|
<input type="radio" v-model="task.type" value="fluid" class="radio radio-primary mr-2">
|
||||||
|
<span>流体传输</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 输入容器配置 -->
|
||||||
|
<div class="container-box">
|
||||||
|
<div class="flex justify-between items-center mb-4">
|
||||||
|
<h3 class="text-lg font-medium text-gray-800">输入容器</h3>
|
||||||
|
<button @click="addInputContainer" class="btn btn-sm btn-primary">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
|
||||||
|
</svg>
|
||||||
|
添加容器
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-for="(input, index) in task.input" :key="'input-'+index" class="mb-6 p-4 bg-gray-50 rounded-lg">
|
||||||
|
<div class="flex justify-between items-center mb-3">
|
||||||
|
<h4 class="font-medium text-gray-700">容器 #{{ index + 1 }}</h4>
|
||||||
|
<button @click="removeInputContainer(index)" class="btn btn-xs btn-error">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-4">
|
||||||
|
<label class="block text-gray-700 mb-1">容器名称</label>
|
||||||
|
<input v-model="input.containerName" type="text" placeholder="例如: chest_left" class="input input-bordered w-full">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-4">
|
||||||
|
<label class="flex items-center">
|
||||||
|
<input type="checkbox" v-model="input.cache" class="checkbox checkbox-primary mr-2">
|
||||||
|
<span>启用缓存模式</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-4">
|
||||||
|
<label class="block text-gray-700 mb-2">过滤规则</label>
|
||||||
|
|
||||||
|
<div class="tabs mb-3">
|
||||||
|
<a class="tab tab-bordered" :class="{'tab-active': input.filterType === 'whitelist'}" @click="input.filterType = 'whitelist'">白名单</a>
|
||||||
|
<a class="tab tab-bordered" :class="{'tab-active': input.filterType === 'blacklist'}" @click="input.filterType = 'blacklist'">黑名单</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="input.filterType === 'whitelist'">
|
||||||
|
<div class="mb-2">
|
||||||
|
<div class="item-entry" v-for="(item, itemIndex) in input.whitelist" :key="'input-wl-'+itemIndex">
|
||||||
|
<input v-model="item.name" type="text" placeholder="物品ID (例如: minecraft:stone)" class="input input-bordered flex-grow">
|
||||||
|
<button @click="removeWhitelistItem(input.whitelist, itemIndex)" class="btn btn-xs btn-error">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button @click="addWhitelistItem(input.whitelist)" class="btn btn-sm btn-outline">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
|
||||||
|
</svg>
|
||||||
|
添加白名单项目
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else>
|
||||||
|
<div class="mb-2">
|
||||||
|
<div class="item-entry" v-for="(item, itemIndex) in input.blacklist" :key="'input-bl-'+itemIndex">
|
||||||
|
<input v-model="item.name" type="text" placeholder="物品ID (例如: minecraft:dirt)" class="input input-bordered flex-grow">
|
||||||
|
<button @click="removeBlacklistItem(input.blacklist, itemIndex)" class="btn btn-xs btn-error">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button @click="addBlacklistItem(input.blacklist)" class="btn btn-sm btn-outline">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
|
||||||
|
</svg>
|
||||||
|
添加黑名单项目
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 输出容器配置 -->
|
||||||
|
<div class="container-box">
|
||||||
|
<div class="flex justify-between items-center mb-4">
|
||||||
|
<h3 class="text-lg font-medium text-gray-800">输出容器</h3>
|
||||||
|
<button @click="addOutputContainer" class="btn btn-sm btn-primary">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
|
||||||
|
</svg>
|
||||||
|
添加容器
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-for="(output, index) in task.output" :key="'output-'+index" class="mb-6 p-4 bg-gray-50 rounded-lg">
|
||||||
|
<div class="flex justify-between items-center mb-3">
|
||||||
|
<h4 class="font-medium text-gray-700">容器 #{{ index + 1 }}</h4>
|
||||||
|
<button @click="removeOutputContainer(index)" class="btn btn-xs btn-error">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-4">
|
||||||
|
<label class="block text-gray-700 mb-1">容器名称</label>
|
||||||
|
<input v-model="output.containerName" type="text" placeholder="例如: chest_right" class="input input-bordered w-full">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-4">
|
||||||
|
<label class="block text-gray-700 mb-1">优先级</label>
|
||||||
|
<input v-model="output.priority" type="number" min="1" class="input input-bordered w-full">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-4">
|
||||||
|
<label class="flex items-center">
|
||||||
|
<input type="checkbox" v-model="output.cache" class="checkbox checkbox-primary mr-2">
|
||||||
|
<span>启用缓存模式</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-4">
|
||||||
|
<label class="block text-gray-700 mb-2">过滤规则</label>
|
||||||
|
|
||||||
|
<div class="tabs mb-3">
|
||||||
|
<a class="tab tab-bordered" :class="{'tab-active': output.filterType === 'whitelist'}" @click="output.filterType = 'whitelist'">白名单</a>
|
||||||
|
<a class="tab tab-bordered" :class="{'tab-active': output.filterType === 'blacklist'}" @click="output.filterType = 'blacklist'">黑名单</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="output.filterType === 'whitelist'">
|
||||||
|
<div class="mb-2">
|
||||||
|
<div class="item-entry" v-for="(item, itemIndex) in output.whitelist" :key="'output-wl-'+itemIndex">
|
||||||
|
<input v-model="item.name" type="text" placeholder="物品ID (例如: minecraft:stone)" class="input input-bordered flex-grow">
|
||||||
|
<input v-model="item.count" type="number" min="1" placeholder="数量" class="input input-bordered w-24">
|
||||||
|
<button @click="removeWhitelistItem(output.whitelist, itemIndex)" class="btn btn-xs btn-error">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button @click="addWhitelistItem(output.whitelist)" class="btn btn-sm btn-outline">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
|
||||||
|
</svg>
|
||||||
|
添加白名单项目
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else>
|
||||||
|
<div class="mb-2">
|
||||||
|
<div class="item-entry" v-for="(item, itemIndex) in output.blacklist" :key="'output-bl-'+itemIndex">
|
||||||
|
<input v-model="item.name" type="text" placeholder="物品ID (例如: minecraft:dirt)" class="input input-bordered flex-grow">
|
||||||
|
<button @click="removeBlacklistItem(output.blacklist, itemIndex)" class="btn btn-xs btn-error">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button @click="addBlacklistItem(output.blacklist)" class="btn btn-sm btn-outline">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
|
||||||
|
</svg>
|
||||||
|
添加黑名单项目
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 右侧预览区域 -->
|
||||||
|
<div class="w-full md:w-1/3">
|
||||||
|
<div class="sticky top-4">
|
||||||
|
<div class="bg-white rounded-xl shadow-lg p-6">
|
||||||
|
<h3 class="text-lg font-semibold text-gray-800 mb-4">配置预览</h3>
|
||||||
|
|
||||||
|
<div class="mb-4">
|
||||||
|
<label class="block text-gray-700 mb-2">生成的任务配置</label>
|
||||||
|
<pre class="bg-gray-800 text-green-200 p-4 rounded-lg overflow-auto max-h-96">{{ formattedJson }}</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 新增命令文本区域 -->
|
||||||
|
<div class="mb-4">
|
||||||
|
<label class="block text-gray-700 mb-2">命令文本</label>
|
||||||
|
<pre class="bg-gray-800 text-blue-200 p-4 rounded-lg overflow-auto">{{ commandString }}</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex gap-2">
|
||||||
|
<button @click="copyToClipboard" class="btn btn-primary flex-grow">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3" />
|
||||||
|
</svg>
|
||||||
|
复制命令
|
||||||
|
</button>
|
||||||
|
<button @click="resetForm" class="btn btn-outline">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
||||||
|
</svg>
|
||||||
|
重置
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="copySuccess" class="mt-3 text-center text-green-500">
|
||||||
|
命令已复制到剪贴板!
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bg-white rounded-xl shadow-lg p-6 mt-4">
|
||||||
|
<h3 class="text-lg font-semibold text-gray-800 mb-4">使用说明</h3>
|
||||||
|
<ul class="text-gray-600 space-y-2">
|
||||||
|
<li class="flex items-start">
|
||||||
|
<div class="bg-blue-100 text-blue-800 rounded-full w-6 h-6 flex items-center justify-center mr-2 flex-shrink-0">1</div>
|
||||||
|
<span>配置输入和输出容器信息</span>
|
||||||
|
</li>
|
||||||
|
<li class="flex items-start">
|
||||||
|
<div class="bg-blue-100 text-blue-800 rounded-full w-6 h-6 flex items-center justify-center mr-2 flex-shrink-0">2</div>
|
||||||
|
<span>设置过滤规则(白名单或黑名单)</span>
|
||||||
|
</li>
|
||||||
|
<li class="flex items-start">
|
||||||
|
<div class="bg-blue-100 text-blue-800 rounded-full w-6 h-6 flex items-center justify-center mr-2 flex-shrink-0">3</div>
|
||||||
|
<span>为输出容器设置优先级</span>
|
||||||
|
</li>
|
||||||
|
<li class="flex items-start">
|
||||||
|
<div class="bg-blue-100 text-blue-800 rounded-full w-6 h-6 flex items-center justify-center mr-2 flex-shrink-0">4</div>
|
||||||
|
<span>复制生成的命令文本</span>
|
||||||
|
</li>
|
||||||
|
<li class="flex items-start">
|
||||||
|
<div class="bg-blue-100 text-blue-800 rounded-full w-6 h-6 flex items-center justify-center mr-2 flex-shrink-0">5</div>
|
||||||
|
<span>在物流系统中粘贴并执行命令</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'App',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
task: {
|
||||||
|
type: 'item',
|
||||||
|
input: [
|
||||||
|
{
|
||||||
|
containerName: '',
|
||||||
|
cache: false,
|
||||||
|
filterType: 'whitelist',
|
||||||
|
whitelist: [{ name: '' }],
|
||||||
|
blacklist: [{ name: '' }]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
output: [
|
||||||
|
{
|
||||||
|
containerName: '',
|
||||||
|
priority: 1,
|
||||||
|
cache: false,
|
||||||
|
filterType: 'whitelist',
|
||||||
|
whitelist: [{ name: '', count: null }],
|
||||||
|
blacklist: [{ name: '' }]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
copySuccess: false,
|
||||||
|
copyDownloadSuccess: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
taskConfig() {
|
||||||
|
const task = JSON.parse(JSON.stringify(this.task));
|
||||||
|
|
||||||
|
// 转换输入容器
|
||||||
|
const inputObj = {};
|
||||||
|
task.input.forEach(container => {
|
||||||
|
const containerName = container.containerName || 'unnamed_container';
|
||||||
|
inputObj[containerName] = {
|
||||||
|
cache: container.cache
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理白名单 - 只有当有实际内容时才添加
|
||||||
|
const whitelistItems = container.whitelist.filter(item => item.name && item.name.trim() !== '');
|
||||||
|
if (whitelistItems.length > 0) {
|
||||||
|
inputObj[containerName].whitelist = {};
|
||||||
|
whitelistItems.forEach(item => {
|
||||||
|
inputObj[containerName].whitelist[item.name] = {};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理黑名单 - 只有当有实际内容时才添加
|
||||||
|
const blacklistItems = container.blacklist.filter(item => item.name && item.name.trim() !== '');
|
||||||
|
if (blacklistItems.length > 0) {
|
||||||
|
inputObj[containerName].blacklist = {};
|
||||||
|
blacklistItems.forEach(item => {
|
||||||
|
inputObj[containerName].blacklist[item.name] = {};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 转换输出容器
|
||||||
|
const outputObj = {};
|
||||||
|
task.output.forEach(container => {
|
||||||
|
const containerName = container.containerName || 'unnamed_container';
|
||||||
|
outputObj[containerName] = {
|
||||||
|
priority: parseInt(container.priority) || 1,
|
||||||
|
cache: container.cache
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理白名单 - 只有当有实际内容时才添加
|
||||||
|
const whitelistItems = container.whitelist.filter(item => item.name && item.name.trim() !== '');
|
||||||
|
if (whitelistItems.length > 0) {
|
||||||
|
outputObj[containerName].whitelist = {};
|
||||||
|
whitelistItems.forEach(item => {
|
||||||
|
if (item.count) {
|
||||||
|
outputObj[containerName].whitelist[item.name] = { count: parseInt(item.count) };
|
||||||
|
} else {
|
||||||
|
outputObj[containerName].whitelist[item.name] = {};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理黑名单 - 只有当有实际内容时才添加
|
||||||
|
const blacklistItems = container.blacklist.filter(item => item.name && item.name.trim() !== '');
|
||||||
|
if (blacklistItems.length > 0) {
|
||||||
|
outputObj[containerName].blacklist = {};
|
||||||
|
blacklistItems.forEach(item => {
|
||||||
|
outputObj[containerName].blacklist[item.name] = {};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: task.type,
|
||||||
|
input: inputObj,
|
||||||
|
output: outputObj
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
formattedJson() {
|
||||||
|
return JSON.stringify(this.taskConfig, null, 2);
|
||||||
|
},
|
||||||
|
|
||||||
|
compressedJson() {
|
||||||
|
return JSON.stringify(this.taskConfig);
|
||||||
|
},
|
||||||
|
|
||||||
|
commandString() {
|
||||||
|
return `add ${this.compressedJson}`;
|
||||||
|
},
|
||||||
|
|
||||||
|
downloadCommand() {
|
||||||
|
return 'wget http://git.liulikeji.cn/xingluo/CCTweaked-utils/raw/branch/main/Logistics/V5/startup.lua';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
addInputContainer() {
|
||||||
|
this.task.input.push({
|
||||||
|
containerName: '',
|
||||||
|
cache: false,
|
||||||
|
filterType: 'whitelist',
|
||||||
|
whitelist: [{ name: '' }],
|
||||||
|
blacklist: [{ name: '' }]
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
removeInputContainer(index) {
|
||||||
|
if (this.task.input.length > 1) {
|
||||||
|
this.task.input.splice(index, 1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
addOutputContainer() {
|
||||||
|
this.task.output.push({
|
||||||
|
containerName: '',
|
||||||
|
priority: 1,
|
||||||
|
cache: false,
|
||||||
|
filterType: 'whitelist',
|
||||||
|
whitelist: [{ name: '', count: null }],
|
||||||
|
blacklist: [{ name: '' }]
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
removeOutputContainer(index) {
|
||||||
|
if (this.task.output.length > 1) {
|
||||||
|
this.task.output.splice(index, 1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
addWhitelistItem(list) {
|
||||||
|
list.push(this.task.type === 'item' ? { name: '', count: null } : { name: '' });
|
||||||
|
},
|
||||||
|
|
||||||
|
removeWhitelistItem(list, index) {
|
||||||
|
if (list.length > 1) {
|
||||||
|
list.splice(index, 1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
addBlacklistItem(list) {
|
||||||
|
list.push({ name: '' });
|
||||||
|
},
|
||||||
|
|
||||||
|
removeBlacklistItem(list, index) {
|
||||||
|
if (list.length > 1) {
|
||||||
|
list.splice(index, 1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
copyToClipboard() {
|
||||||
|
const textarea = document.createElement('textarea');
|
||||||
|
textarea.value = this.commandString;
|
||||||
|
document.body.appendChild(textarea);
|
||||||
|
textarea.select();
|
||||||
|
document.execCommand('copy');
|
||||||
|
document.body.removeChild(textarea);
|
||||||
|
|
||||||
|
this.copySuccess = true;
|
||||||
|
setTimeout(() => {
|
||||||
|
this.copySuccess = false;
|
||||||
|
}, 3000);
|
||||||
|
},
|
||||||
|
|
||||||
|
copyDownloadCommand() {
|
||||||
|
const textarea = document.createElement('textarea');
|
||||||
|
textarea.value = this.downloadCommand;
|
||||||
|
document.body.appendChild(textarea);
|
||||||
|
textarea.select();
|
||||||
|
document.execCommand('copy');
|
||||||
|
document.body.removeChild(textarea);
|
||||||
|
|
||||||
|
this.copyDownloadSuccess = true;
|
||||||
|
setTimeout(() => {
|
||||||
|
this.copyDownloadSuccess = false;
|
||||||
|
}, 3000);
|
||||||
|
},
|
||||||
|
|
||||||
|
resetForm() {
|
||||||
|
this.task = {
|
||||||
|
type: 'item',
|
||||||
|
input: [
|
||||||
|
{
|
||||||
|
containerName: '',
|
||||||
|
cache: false,
|
||||||
|
filterType: 'whitelist',
|
||||||
|
whitelist: [{ name: '' }],
|
||||||
|
blacklist: [{ name: '' }]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
output: [
|
||||||
|
{
|
||||||
|
containerName: '',
|
||||||
|
priority: 1,
|
||||||
|
cache: false,
|
||||||
|
filterType: 'whitelist',
|
||||||
|
whitelist: [{ name: '', count: null }],
|
||||||
|
blacklist: [{ name: '' }]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.container-box {
|
||||||
|
background-color: #f9fafb;
|
||||||
|
border: 1px solid #e5e7eb;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
padding: 1.5rem;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
.item-entry {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
.drag-handle {
|
||||||
|
cursor: move;
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
.drag-handle:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
.transition-all {
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
1
Logistics/V5/web/src/assets/vue.svg
Normal file
1
Logistics/V5/web/src/assets/vue.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>
|
||||||
|
After Width: | Height: | Size: 496 B |
5
Logistics/V5/web/src/main.js
Normal file
5
Logistics/V5/web/src/main.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { createApp } from 'vue'
|
||||||
|
import './style.css'
|
||||||
|
import App from './App.vue'
|
||||||
|
|
||||||
|
createApp(App).mount('#app')
|
||||||
18
Logistics/V5/web/src/router/index.js
Normal file
18
Logistics/V5/web/src/router/index.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { createRouter, createWebHistory } from 'vue-router';
|
||||||
|
import BlueprintEditorView from '@/views/BlueprintEditorView.vue';
|
||||||
|
|
||||||
|
const routes = [
|
||||||
|
{
|
||||||
|
path: '/blueprint-editor',
|
||||||
|
name: 'BlueprintEditor',
|
||||||
|
component: BlueprintEditorView
|
||||||
|
},
|
||||||
|
// 其他路由...
|
||||||
|
];
|
||||||
|
|
||||||
|
const router = createRouter({
|
||||||
|
history: createWebHistory(process.env.BASE_URL),
|
||||||
|
routes
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
||||||
2
Logistics/V5/web/src/style.css
Normal file
2
Logistics/V5/web/src/style.css
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
@import "tailwindcss";
|
||||||
|
@plugin "daisyui";
|
||||||
7
Logistics/V5/web/vite.config.js
Normal file
7
Logistics/V5/web/vite.config.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import vue from '@vitejs/plugin-vue'
|
||||||
|
import tailwindcss from '@tailwindcss/vite';
|
||||||
|
// https://vite.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [vue(),tailwindcss()],
|
||||||
|
})
|
||||||
@@ -56,18 +56,23 @@ local function parseItemRequirements(input)
|
|||||||
|
|
||||||
-- Split key-value pairs
|
-- Split key-value pairs
|
||||||
for pair in content:gmatch("[^,]+") do
|
for pair in content:gmatch("[^,]+") do
|
||||||
|
-- 支持JSON格式: "key":value
|
||||||
local key, value = pair:match("[\"']?(.-)[\"']?%s*:%s*(%d+)")
|
local key, value = pair:match("[\"']?(.-)[\"']?%s*:%s*(%d+)")
|
||||||
if key and value then
|
if key and value then
|
||||||
requirements[key] = tonumber(value)
|
requirements[key] = tonumber(value)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
-- Simple format: item:minCount
|
-- 只支持等号格式: item=count
|
||||||
for item in input:gmatch("%S+") do
|
for item in input:gmatch("%S+") do
|
||||||
local name, minCount = item:match("([^:]+):(%d+)")
|
-- 匹配等号格式: modid:itemid=count
|
||||||
|
local name, minCount = item:match("([^=]+)=(%d+)")
|
||||||
|
|
||||||
|
-- 如果匹配到等号格式
|
||||||
if name and minCount then
|
if name and minCount then
|
||||||
requirements[name] = tonumber(minCount)
|
requirements[name] = tonumber(minCount)
|
||||||
else
|
else
|
||||||
|
-- 没有等号,视为只有物品名
|
||||||
requirements[item] = 0 -- No minimum requirement
|
requirements[item] = 0 -- No minimum requirement
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -87,18 +92,23 @@ local function parseFluidRequirements(input)
|
|||||||
|
|
||||||
-- Split key-value pairs
|
-- Split key-value pairs
|
||||||
for pair in content:gmatch("[^,]+") do
|
for pair in content:gmatch("[^,]+") do
|
||||||
|
-- 支持JSON格式: "key":value
|
||||||
local key, value = pair:match("[\"']?(.-)[\"']?%s*:%s*(%d+)")
|
local key, value = pair:match("[\"']?(.-)[\"']?%s*:%s*(%d+)")
|
||||||
if key and value then
|
if key and value then
|
||||||
requirements[key] = tonumber(value)
|
requirements[key] = tonumber(value)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
-- Simple format: fluid:minAmount
|
-- 只支持等号格式: fluid=minAmount
|
||||||
for fluid in input:gmatch("%S+") do
|
for fluid in input:gmatch("%S+") do
|
||||||
local name, minAmount = fluid:match("([^:]+):(%d+)")
|
-- 匹配等号格式: fluid_name=minAmount
|
||||||
|
local name, minAmount = fluid:match("([^=]+)=(%d+)")
|
||||||
|
|
||||||
|
-- 如果匹配到等号格式
|
||||||
if name and minAmount then
|
if name and minAmount then
|
||||||
requirements[name] = tonumber(minAmount)
|
requirements[name] = tonumber(minAmount)
|
||||||
else
|
else
|
||||||
|
-- 没有等号,视为只有流体名
|
||||||
requirements[fluid] = 0 -- No minimum requirement
|
requirements[fluid] = 0 -- No minimum requirement
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -488,7 +498,7 @@ local function processCommand(cmd)
|
|||||||
local fluidsList = {}
|
local fluidsList = {}
|
||||||
for name, minAmount in pairs(task.fluids) do
|
for name, minAmount in pairs(task.fluids) do
|
||||||
if minAmount > 0 then
|
if minAmount > 0 then
|
||||||
table.insert(fluidsList, name..":"..minAmount.."mB")
|
table.insert(fluidsList, name.."="..minAmount.."mB")
|
||||||
else
|
else
|
||||||
table.insert(fluidsList, name)
|
table.insert(fluidsList, name)
|
||||||
end
|
end
|
||||||
@@ -502,7 +512,7 @@ local function processCommand(cmd)
|
|||||||
local itemsList = {}
|
local itemsList = {}
|
||||||
for name, minCount in pairs(task.items) do
|
for name, minCount in pairs(task.items) do
|
||||||
if minCount > 0 then
|
if minCount > 0 then
|
||||||
table.insert(itemsList, name..":"..minCount)
|
table.insert(itemsList, name.."="..minCount)
|
||||||
else
|
else
|
||||||
table.insert(itemsList, name)
|
table.insert(itemsList, name)
|
||||||
end
|
end
|
||||||
@@ -533,13 +543,11 @@ local function processCommand(cmd)
|
|||||||
print("Available commands:")
|
print("Available commands:")
|
||||||
print("add <source> <destination> [items] - Add item transfer task")
|
print("add <source> <destination> [items] - Add item transfer task")
|
||||||
print(" Items format:")
|
print(" Items format:")
|
||||||
print(" Simple: item1[:minCount] item2[:minCount] ...")
|
print(" Simple: item1[=minCount] item2[=minCount] ...")
|
||||||
print(" JSON-like: {\"item1\":minCount, \"item2\":minCount}")
|
|
||||||
print(" Set minCount to 0 to transfer all available items")
|
print(" Set minCount to 0 to transfer all available items")
|
||||||
print("addfluid <source> <destination> [fluids] - Add fluid transfer task")
|
print("addfluid <source> <destination> [fluids] - Add fluid transfer task")
|
||||||
print(" Fluids format:")
|
print(" Fluids format:")
|
||||||
print(" Simple: fluid1[:minAmount] fluid2[:minAmount] ...")
|
print(" Simple: fluid1[=minAmount] fluid2[:minAmount] ...")
|
||||||
print(" JSON-like: {\"fluid1\":minAmount, \"fluid2\":minAmount}")
|
|
||||||
print(" Set minAmount to 0 to transfer all available fluid")
|
print(" Set minAmount to 0 to transfer all available fluid")
|
||||||
print("list - List all tasks")
|
print("list - List all tasks")
|
||||||
print("remove <index> - Remove task")
|
print("remove <index> - Remove task")
|
||||||
|
|||||||
Reference in New Issue
Block a user