1 | using System;
|
---|
2 | using System.Collections.Generic;
|
---|
3 | using System.Linq;
|
---|
4 | using Bridge;
|
---|
5 | using Bridge.Html5;
|
---|
6 | using Bridge.jQuery2;
|
---|
7 |
|
---|
8 | namespace WebMrbc
|
---|
9 | {
|
---|
10 | delegate bool jQueryKeyboardHandler(jQueryKeyboardEvent e);
|
---|
11 |
|
---|
12 | public static class App
|
---|
13 | {
|
---|
14 | public static Mruby Module;
|
---|
15 | public static Terminal Term;
|
---|
16 | public static AceEditor CodeEditor;
|
---|
17 | public static TargetBoard TargetBoard;
|
---|
18 | public static bool changedAfterTranslating;
|
---|
19 | public static bool translating;
|
---|
20 | public static JsonClassInfo NodeProfileClass;
|
---|
21 | public static JsonClassGroupInfo[] ClassGroups;
|
---|
22 | public static JsonPropertyInfo[] BaseObjectPropertyList;
|
---|
23 | public static string ArduinoToolbox;
|
---|
24 | public static string EcnlToolbox;
|
---|
25 |
|
---|
26 | [Ready]
|
---|
27 | public static void Main()
|
---|
28 | {
|
---|
29 | Script.Write("Number.isFinite = Number.isFinite || function(any) { return typeof any === 'number' && isFinite(any); }");
|
---|
30 | Script.Write("Number.isNaN = Number.isNaN || function(any) { return typeof any === 'number' && isNaN(any); }");
|
---|
31 |
|
---|
32 | App.TargetBoard = new GrPeach();
|
---|
33 | Collections.ClassWorkspaces = new Collection<IClassWorkspace>();
|
---|
34 | Views.ClassSelectorView = new ClassSelectorView();
|
---|
35 | Views.ClassSelectorView.SetCollection(Collections.ClassWorkspaces);
|
---|
36 | Views.EObjectModalView = new EObjectModalView();
|
---|
37 | Views.MainMenuView = new MainMenuView();
|
---|
38 |
|
---|
39 | var termElement = Document.GetElementById("term");
|
---|
40 | Term = new Terminal(new TerminalOption() {
|
---|
41 | cols = 80,
|
---|
42 | rows = 24,
|
---|
43 | useStyle = true,
|
---|
44 | screenKeys = true,
|
---|
45 | cursorBlink = false
|
---|
46 | });
|
---|
47 | Term.on("data", new Action<object>((data) => {
|
---|
48 | if (Module != null && Module["stdin"] != null)
|
---|
49 | Module.stdin(data);
|
---|
50 | }));
|
---|
51 | Term.on("title", new Action<string>((title) => {
|
---|
52 | Document.Title = title;
|
---|
53 | }));
|
---|
54 |
|
---|
55 | Term.open(termElement);
|
---|
56 |
|
---|
57 | CodeEditor = ace.edit("text-editor");
|
---|
58 | Window.Set("textEditor", CodeEditor);
|
---|
59 | CodeEditor.setTheme("ace/theme/twilight");
|
---|
60 | CodeEditor.setShowInvisibles(true);
|
---|
61 | CodeEditor.gotoLine(0, 0);
|
---|
62 | CodeEditor.On("change", new Action(() => {
|
---|
63 | if (!translating) {
|
---|
64 | changedAfterTranslating = true;
|
---|
65 | }
|
---|
66 | }));
|
---|
67 | var session = CodeEditor.getSession();
|
---|
68 | session.setMode("ace/mode/ruby");
|
---|
69 | session.setTabSize(2);
|
---|
70 | session.setUseSoftTabs(false);
|
---|
71 |
|
---|
72 | var statusElement = Document.GetElementById("status");
|
---|
73 | var progressElement = Document.GetElementById("progress");
|
---|
74 | var spinnerElement = Document.GetElementById("spinner");
|
---|
75 |
|
---|
76 | statusElement.InnerHTML = "Downloading...";
|
---|
77 |
|
---|
78 | jQuery.Ready(() => {
|
---|
79 | InitClassGroups((result) => {
|
---|
80 | if (result) {
|
---|
81 | Views.EObjectModalView.InitClassGroups();
|
---|
82 | }
|
---|
83 | InitArduinoToolbox((result1) => {
|
---|
84 | InitEcnlToolbox((result2) => {
|
---|
85 | if (result1 && result2) {
|
---|
86 | AddMainLoop();
|
---|
87 | AddEcnlTask();
|
---|
88 | AddENode();
|
---|
89 | }
|
---|
90 | statusElement.InnerHTML = "";
|
---|
91 | spinnerElement.Style.Display = Display.None;
|
---|
92 | });
|
---|
93 | });
|
---|
94 | });
|
---|
95 | });
|
---|
96 | }
|
---|
97 |
|
---|
98 | public static void InitClassGroups(Action<bool> action)
|
---|
99 | {
|
---|
100 | jQuery.Ajax(new AjaxOptions() {
|
---|
101 | Url = "echonet_objects.json",
|
---|
102 | Success = (data, textStatus, request) => {
|
---|
103 | ClassGroupsSuccess(data, textStatus, request);
|
---|
104 | action?.Invoke(true);
|
---|
105 | },
|
---|
106 | Error = (request, textStatus, error) => {
|
---|
107 | AjaxError(request, textStatus, error);
|
---|
108 | action?.Invoke(false);
|
---|
109 | }
|
---|
110 | });
|
---|
111 | }
|
---|
112 |
|
---|
113 | private static void ClassGroupsSuccess(object data, string textStatus, jqXHR request)
|
---|
114 | {
|
---|
115 | var _deviceInfo = (dynamic)((data.GetType() == typeof(string)) ? jQuery.ParseJSON((string)data) : data);
|
---|
116 | var profProps = new JsonPropertyInfo[0];
|
---|
117 | foreach (var property in _deviceInfo.baseProfilePropertyList) {
|
---|
118 | profProps.Push(new JsonPropertyInfo(property));
|
---|
119 | }
|
---|
120 | var objProps = new JsonPropertyInfo[0];
|
---|
121 | foreach (var property in _deviceInfo.baseObjectPropertyList) {
|
---|
122 | objProps.Push(new JsonPropertyInfo(property));
|
---|
123 | }
|
---|
124 | BaseObjectPropertyList = objProps;
|
---|
125 |
|
---|
126 | var classGroups = new JsonClassGroupInfo[0];
|
---|
127 | foreach (var _item in _deviceInfo.classGroupList) {
|
---|
128 | var item = new JsonClassGroupInfo(_item);
|
---|
129 | classGroups.Push(item);
|
---|
130 | var classes = new JsonClassInfo[0];
|
---|
131 | foreach (var _cls in _item.classList) {
|
---|
132 | var cls = new JsonClassInfo(_cls);
|
---|
133 | cls.classGroup = item;
|
---|
134 | classes.Push(cls);
|
---|
135 | var properties = new JsonPropertyInfo[0];
|
---|
136 | foreach (var property in _cls.propertyList) {
|
---|
137 | properties.Push(new JsonPropertyInfo(property));
|
---|
138 | }
|
---|
139 | if ((item.classGroupCode == 0xE) && (cls.classCode == 0xF0)) {
|
---|
140 | cls.properties = (JsonPropertyInfo[])profProps.Concat(properties);
|
---|
141 | NodeProfileClass = cls;
|
---|
142 | }
|
---|
143 | else {
|
---|
144 | cls.properties = properties;
|
---|
145 | }
|
---|
146 | }
|
---|
147 | item.classes = classes;
|
---|
148 | }
|
---|
149 | ClassGroups = classGroups;
|
---|
150 | }
|
---|
151 |
|
---|
152 | public static void InitArduinoToolbox(Action<bool> action)
|
---|
153 | {
|
---|
154 | jQuery.Ajax(new AjaxOptions() {
|
---|
155 | Url = "arduino_toolbox.xml",
|
---|
156 | DataType = "text",
|
---|
157 | Success = (data, textStatus, request) => {
|
---|
158 | ArduinoToolbox = (string)data;
|
---|
159 | action?.Invoke(true);
|
---|
160 | },
|
---|
161 | Error = (request, textStatus, error) => {
|
---|
162 | AjaxError(request, textStatus, error);
|
---|
163 | action?.Invoke(false);
|
---|
164 | }
|
---|
165 | });
|
---|
166 | }
|
---|
167 |
|
---|
168 | public static void InitEcnlToolbox(Action<bool> action)
|
---|
169 | {
|
---|
170 | jQuery.Ajax(new AjaxOptions() {
|
---|
171 | Url = "ecnl_toolbox.xml",
|
---|
172 | DataType = "text",
|
---|
173 | Success = (data, textStatus, request) => {
|
---|
174 | EcnlToolbox = (string)data;
|
---|
175 | action?.Invoke(true);
|
---|
176 | },
|
---|
177 | Error = (request, textStatus, error) => {
|
---|
178 | AjaxError(request, textStatus, error);
|
---|
179 | action?.Invoke(false);
|
---|
180 | }
|
---|
181 | });
|
---|
182 | }
|
---|
183 |
|
---|
184 | private static void AjaxError(jqXHR request, string textStatus, string error)
|
---|
185 | {
|
---|
186 | }
|
---|
187 |
|
---|
188 | /// <summary>
|
---|
189 | /// テキスト入力欄のEnter(Return)キーを無視する
|
---|
190 | /// </summary>
|
---|
191 | /// <param name="el"></param>
|
---|
192 | /// <returns></returns>
|
---|
193 | static jQuery ignoreEnterKey(jQuery el)
|
---|
194 | {
|
---|
195 | return el.Find("input[type=text]").KeyPress(new jQueryKeyboardHandler((e) => {
|
---|
196 | if (e == null)
|
---|
197 | e = (jQueryKeyboardEvent)Window.Get("Event");
|
---|
198 | if (e.KeyCode == 13)
|
---|
199 | return false;
|
---|
200 | else
|
---|
201 | return true;
|
---|
202 | }));
|
---|
203 | }
|
---|
204 |
|
---|
205 | private static void AddMainLoop()
|
---|
206 | {
|
---|
207 | var view = Views.MainMenuView.NewBlocklyView("MainLoop");
|
---|
208 | var workspace = new MainLoopWorkspace(view);
|
---|
209 | Collections.MainLoopWorkspace = workspace;
|
---|
210 | Collections.ClassWorkspaces.Add(workspace);
|
---|
211 | Views.ClassSelectorView.SelectClassWorkspace(workspace);
|
---|
212 | view.ReloadToolbox(workspace);
|
---|
213 | }
|
---|
214 |
|
---|
215 | private static void AddEcnlTask()
|
---|
216 | {
|
---|
217 | var identifier = Collections.ClassWorkspaces.UniqueName("EcnlTask");
|
---|
218 | var view = Views.MainMenuView.NewBlocklyView(identifier);
|
---|
219 | var workspace = new EcnlTaskWorkspace(view);
|
---|
220 | Collections.EcnlTaskWorkspace = workspace;
|
---|
221 | Collections.ClassWorkspaces.Add(workspace);
|
---|
222 | Views.ClassSelectorView.SelectClassWorkspace(workspace);
|
---|
223 | view.ReloadToolbox(workspace);
|
---|
224 | }
|
---|
225 |
|
---|
226 | private static void AddENode()
|
---|
227 | {
|
---|
228 | var identifier = Collections.ClassWorkspaces.UniqueName("LocalNode");
|
---|
229 | var localNode = new JsonNodeInfo(NodeProfileClass, identifier, "local");
|
---|
230 | var properties = new JsonPropertyInfo[0];
|
---|
231 | foreach (var item in localNode.type.properties) {
|
---|
232 | if ((item.required.Length > 0) && (item.required[0] != "NONE")) {
|
---|
233 | properties.Push(new JsonPropertyInfo(item));
|
---|
234 | }
|
---|
235 | }
|
---|
236 | localNode.properties = properties;
|
---|
237 | var view = Views.MainMenuView.NewBlocklyView(localNode.identifier);
|
---|
238 | var workspace = new ENodeWorkspace(view, localNode);
|
---|
239 | Collections.LocalNode = workspace;
|
---|
240 | Collections.ClassWorkspaces.Add(workspace);
|
---|
241 | Views.ClassSelectorView.SelectClassWorkspace(workspace);
|
---|
242 | view.BlockCreated += workspace.OnBlockCreated;
|
---|
243 | view.BlockDeleted += workspace.OnBlockDeleted;
|
---|
244 | view.BlockChanged += workspace.OnBlockChanged;
|
---|
245 | view.BlockMoveed += workspace.OnBlockMoveed;
|
---|
246 | view.ReloadToolbox(workspace);
|
---|
247 | }
|
---|
248 |
|
---|
249 | internal static void NewItem(Action<IClassWorkspace> callback)
|
---|
250 | {
|
---|
251 | var identifier = Collections.ClassWorkspaces.UniqueName("Kaden");
|
---|
252 | var eobject = new JsonObjectInfo(App.NodeProfileClass, identifier);
|
---|
253 | var properties = new JsonPropertyInfo[0];
|
---|
254 | if ((eobject.type.classGroup.classGroupCode != 0x0E)
|
---|
255 | && (eobject.type.classCode != 0xF0)) {
|
---|
256 | foreach (var item in App.BaseObjectPropertyList) {
|
---|
257 | if ((item.required.Length > 0) && (item.required[0] != "NONE")) {
|
---|
258 | properties.Push(new JsonPropertyInfo(item));
|
---|
259 | }
|
---|
260 | }
|
---|
261 | }
|
---|
262 | foreach (var item in eobject.type.properties) {
|
---|
263 | if ((item.required.Length > 0) && (item.required[0] != "NONE")) {
|
---|
264 | properties.Push(new JsonPropertyInfo(item));
|
---|
265 | }
|
---|
266 | }
|
---|
267 | eobject.properties = properties;
|
---|
268 | var view = Views.MainMenuView.NewBlocklyView(eobject.identifier);
|
---|
269 | var workspace = new EObjectWorkspace(view, eobject);
|
---|
270 | view.BlockCreated += workspace.OnBlockCreated;
|
---|
271 | view.BlockDeleted += workspace.OnBlockDeleted;
|
---|
272 | view.BlockChanged += workspace.OnBlockChanged;
|
---|
273 | view.BlockMoveed += workspace.OnBlockMoveed;
|
---|
274 | callback(workspace);
|
---|
275 | }
|
---|
276 |
|
---|
277 | internal static void RemoveItem(IClassWorkspace item)
|
---|
278 | {
|
---|
279 | Views.MainMenuView.RemoveEObjectWorkspace(item);
|
---|
280 | }
|
---|
281 |
|
---|
282 | internal static void Write(string text)
|
---|
283 | {
|
---|
284 | Term?.write(text.Replace("\n", "\r\n"));
|
---|
285 | }
|
---|
286 |
|
---|
287 | internal static void WriteLine(string text)
|
---|
288 | {
|
---|
289 | Term?.write(text.Replace("\n", "\r\n") + "\r\n");
|
---|
290 | }
|
---|
291 | }
|
---|
292 |
|
---|
293 | public interface IClassWorkspace : IModel
|
---|
294 | {
|
---|
295 | Workspace Workspace { get; }
|
---|
296 | BlocklyView View { get; }
|
---|
297 | Ruby RubyCode { get; }
|
---|
298 | string GetImageUrl();
|
---|
299 | bool IsPreset();
|
---|
300 | string ToCode(string filename);
|
---|
301 | void Activate();
|
---|
302 | void Inactivate();
|
---|
303 | void ReloadToolbox(HTMLElement toolbox);
|
---|
304 | void OpenModifyView(Action<bool> callback);
|
---|
305 | string Template(string template);
|
---|
306 | }
|
---|
307 |
|
---|
308 | public class Collections
|
---|
309 | {
|
---|
310 | [Name(false)]
|
---|
311 | internal static ENodeWorkspace LocalNode;
|
---|
312 | [Name(false)]
|
---|
313 | internal static Collection<IClassWorkspace> ClassWorkspaces;
|
---|
314 | [Name(false)]
|
---|
315 | internal static EcnlTaskWorkspace EcnlTaskWorkspace;
|
---|
316 | [Name(false)]
|
---|
317 | internal static MainLoopWorkspace MainLoopWorkspace;
|
---|
318 | }
|
---|
319 |
|
---|
320 | public class Views
|
---|
321 | {
|
---|
322 | [Name(false)]
|
---|
323 | internal static MainMenuView MainMenuView;
|
---|
324 | [Name(false)]
|
---|
325 | internal static ClassSelectorView ClassSelectorView;
|
---|
326 | [Name(false)]
|
---|
327 | internal static EObjectModalView EObjectModalView;
|
---|
328 | }
|
---|
329 |
|
---|
330 | public class HexDump
|
---|
331 | {
|
---|
332 | string text;
|
---|
333 |
|
---|
334 | public HexDump(Uint8Array bytes, int width)
|
---|
335 | {
|
---|
336 | var sb = new string[0];
|
---|
337 |
|
---|
338 | for (int index = 0; index < bytes.Length; index += width) {
|
---|
339 | sb.Push(String.Format("{0:X4} : ", index)
|
---|
340 | + BinBump(bytes, index, width)
|
---|
341 | + AsciiDump(bytes, index, width));
|
---|
342 | }
|
---|
343 |
|
---|
344 | text = sb.Join("\n");
|
---|
345 | }
|
---|
346 |
|
---|
347 | private string BinBump(Uint8Array bytes, int offset, int width)
|
---|
348 | {
|
---|
349 | var sb = new System.Text.StringBuilder();
|
---|
350 |
|
---|
351 | for (int index = 0; index < width; index++) {
|
---|
352 | if (index + offset < bytes.Length) {
|
---|
353 | sb.AppendFormat("{0:X2} ", bytes[index + offset]);
|
---|
354 | }
|
---|
355 | else {
|
---|
356 | sb.Append(" ");
|
---|
357 | }
|
---|
358 | }
|
---|
359 |
|
---|
360 | return sb.ToString();
|
---|
361 | }
|
---|
362 |
|
---|
363 | private string AsciiDump(Uint8Array bytes, int index, int width)
|
---|
364 | {
|
---|
365 | var sb = "";
|
---|
366 |
|
---|
367 | if (index < bytes.Length) {
|
---|
368 | width = System.Math.Min(width, bytes.Length - index);
|
---|
369 |
|
---|
370 | sb += ": ";
|
---|
371 | for (int i = 0; i < width; i++) {
|
---|
372 | byte b = bytes[i + index];
|
---|
373 | if (b < 0x20)
|
---|
374 | sb += "\x1B[1;3;31m" + String.FromCharCode(b + 0x40) + "\x1B[0m";
|
---|
375 | else
|
---|
376 | sb += String.FromCharCode(b);
|
---|
377 | }
|
---|
378 | }
|
---|
379 | else {
|
---|
380 | sb += ": ";
|
---|
381 | }
|
---|
382 |
|
---|
383 | return sb;
|
---|
384 | }
|
---|
385 |
|
---|
386 | public override string ToString()
|
---|
387 | {
|
---|
388 | return text;
|
---|
389 | }
|
---|
390 | }
|
---|
391 | }
|
---|